diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/CMakeLists.txt b/projects/rocprofiler-systems/source/lib/core/trace_cache/CMakeLists.txt index bd632d886d4e..46b874c13c37 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/CMakeLists.txt @@ -14,6 +14,8 @@ set(trace_cache_sources ) set(trace_cache_headers + ${CMAKE_CURRENT_LIST_DIR}/archive.hpp + ${CMAKE_CURRENT_LIST_DIR}/record_header.hpp ${CMAKE_CURRENT_LIST_DIR}/cache_manager.hpp ${CMAKE_CURRENT_LIST_DIR}/data_types.hpp ${CMAKE_CURRENT_LIST_DIR}/discovery.hpp diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/archive.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/archive.hpp new file mode 100644 index 000000000000..c4ef0705bd4b --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/archive.hpp @@ -0,0 +1,164 @@ +// Copyright (c) Advanced Micro Devices, Inc. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "core/trace_cache/cache_type_traits.hpp" +#include "core/trace_cache/cacheable.hpp" + +#include +#include +#include +#include + +namespace rocprofsys::trace_cache +{ + +static_assert(std::is_same_v, + "trace cache format assumes size_t is std::uint64_t"); + +/** + * True when Type itself declares `void serialize(Archive&)`. A serialize inherited from a + * base class, or one that takes the archive by value, does not count: either would + * compile and silently skip the fields of the derived record or lose the position. + */ +template +concept archive_serializable = + std::is_same_v), + void (Type::*)(Archive&)>; + +struct binary_size_archive +{ + std::size_t size = 0; + + template + constexpr void operator()(const Types&... values) + { + (measure_value(values), ...); + } + +private: + template + constexpr void measure_value(const Type& value) + { + if constexpr(archive_serializable) + { + const_cast(value).serialize(*this); + } + else + { + size += utility::get_size(value); + } + } +}; + +struct binary_write_archive +{ + std::uint8_t* buffer = nullptr; + std::size_t position = 0; + + template + void operator()(const Types&... values) + { + // Locals: writes through the byte buffer may alias the members. + std::uint8_t* const out = buffer; + std::size_t pos = position; + (write_value(values, out, pos), ...); + position = pos; + } + +private: + template + void write_value(const Type& value, std::uint8_t* out, std::size_t& pos) + { + if constexpr(archive_serializable) + { + position = pos; + const_cast(value).serialize(*this); + pos = position; + } + else + { + utility::store_value(value, out, pos); + } + } +}; + +/** + * string_view fields are read as views into the buffer. Stored strings end with a '\0' + * that consumers rely on (for example rocpd_processor passes `.data()` as a C string). + */ +struct binary_read_archive +{ + std::uint8_t* cursor = nullptr; + + template + void operator()(Types&... values) + { + std::uint8_t* cur = cursor; + (read_value(values, cur), ...); + cursor = cur; + } + +private: + template + void read_value(Type& value, std::uint8_t*& cur) + { + if constexpr(archive_serializable) + { + cursor = cur; + value.serialize(*this); + cur = cursor; + } + else + { + utility::parse_value(cur, value); + } + } +}; + +template +concept archive_record = + std::is_default_constructible_v && requires { T::type_identifier; } && + archive_serializable && + archive_serializable && + archive_serializable; + +// const_cast: the size and write archives only read through serialize(). +// flatten, not inline (inlining drops it): one fully inlined function per record type. +template +[[nodiscard, gnu::flatten]] std::size_t +get_size(const T& record) +{ + binary_size_archive archive; + const_cast(record).serialize(archive); + return archive.size; +} + +template +[[gnu::flatten]] void +serialize(std::uint8_t* buffer, const T& record) +{ + binary_write_archive archive{ .buffer = buffer, .position = 0 }; + const_cast(record).serialize(archive); +} + +template +[[nodiscard, gnu::flatten]] T +deserialize(std::uint8_t*& buffer) +{ + // Default-initialized like the old deserializers: zeroing it first was slower. + T record; + binary_read_archive archive{ .cursor = buffer }; + record.serialize(archive); + buffer = archive.cursor; + return record; +} + +namespace type_traits +{ +template +concept cacheable = archive_record && has_type_identifier; +} // namespace type_traits + +} // namespace rocprofsys::trace_cache diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/buffer_storage.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/buffer_storage.cpp index 24589d27a78a..69ec01826ba4 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/buffer_storage.cpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/buffer_storage.cpp @@ -73,7 +73,7 @@ flush_worker_t::start(const pid_t& current_pid) // heap-backed condition-variable state. Polling at the existing // 10 ms flush interval bounds shutdown latency without that // callback lifetime race. - wait_condition.wait_for(wait_lock, CACHE_FILE_FLUSH_TIMEOUT, + wait_condition.wait_for(wait_lock, k_cache_file_flush_timeout, [&]() { return stoken.stop_requested(); }); } diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/buffer_storage.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/buffer_storage.hpp index 19f0cb36f03d..e007f2a059ed 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/buffer_storage.hpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/buffer_storage.hpp @@ -4,8 +4,10 @@ #pragma once #include "core/state.hpp" +#include "core/trace_cache/archive.hpp" #include "core/trace_cache/cache_type_traits.hpp" #include "core/trace_cache/cacheable.hpp" +#include "core/trace_cache/record_header.hpp" #include "policies/thread_state_policy.hpp" @@ -137,11 +139,8 @@ class buffer_storage "Trying to use buffered storage while it is not running"); } - using TypeIdentifierEnumUderlayingType = - std::underlying_type_t; - - size_t const sample_size = get_size(value); - size_t const bytes_to_reserve = header_size + sample_size; + const record_header header{ .type = Type::type_identifier, + .size = get_size(value) }; // Hold the mutex for the entire reserve-and-write operation so that // the flush worker thread never reads a buffer region whose write is @@ -153,14 +152,8 @@ class buffer_storage ThreadStatePolicy::scoped(ThreadStatePolicy::Internal); std::lock_guard const scope{ m_mutex }; - auto* buf = reserve_memory_space(bytes_to_reserve); - size_t position = 0; - auto const type_identifier_value = - static_cast(Type::type_identifier); - - utility::store_value(type_identifier_value, buf, position); - utility::store_value(sample_size, buf, position); - serialize(buf + position, value); + auto* buf = reserve_memory_space(k_header_size + header.size); + write_record(buf, header, value); } [[nodiscard]] __attribute__((always_inline)) bool is_running() const @@ -187,8 +180,8 @@ class buffer_storage } auto const used_space = - _head > _tail ? (_head - _tail) : (buffer_size - _tail + _head); - if(!force && used_space < flush_threshold) + _head > _tail ? (_head - _tail) : (k_buffer_size - _tail + _head); + if(!force && used_space < k_flush_threshold) { return; } @@ -202,7 +195,7 @@ class buffer_storage else { ofs.write(reinterpret_cast(m_buffer->data() + _tail), - buffer_size - _tail); + k_buffer_size - _tail); ofs.write(reinterpret_cast(m_buffer->data()), _head); } if(ofs.fail()) @@ -217,14 +210,14 @@ class buffer_storage { auto* _data = m_buffer->data(); memset(_data + m_head, std::numeric_limits::max(), - buffer_size - m_head); - - const TypeIdentifierEnum tag = TypeIdentifierEnum::fragmented_space; - std::memcpy(_data + m_head, &tag, sizeof(TypeIdentifierEnum)); + k_buffer_size - m_head); - size_t remaining_bytes = buffer_size - m_head - header_size; - std::memcpy(_data + m_head + sizeof(TypeIdentifierEnum), &remaining_bytes, - sizeof(size_t)); + const record_header header{ + .type = TypeIdentifierEnum::fragmented_space, + .size = k_buffer_size - m_head - k_header_size + }; + binary_write_archive archive{ .buffer = _data + m_head, .position = 0 }; + archive(header); m_head = 0; } @@ -233,8 +226,8 @@ class buffer_storage [[nodiscard]] __attribute__((always_inline)) std::uint8_t* reserve_memory_space( const size_t& number_of_bytes) { - if(__builtin_expect((m_head + number_of_bytes + header_size) > - buffer_size, + if(__builtin_expect((m_head + number_of_bytes + + k_header_size) > k_buffer_size, 0)) { fragment_memory(); diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/cache_manager.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/cache_manager.cpp index e197b0ee384d..556b2fb04778 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/cache_manager.cpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/cache_manager.cpp @@ -6,6 +6,7 @@ #include "core/config.hpp" #include "core/perfetto/cached_perfetto_session.hpp" #include "core/timemory.hpp" +#include "core/trace_cache/cacheable.hpp" #include "core/trace_cache/data_types.hpp" #include "core/trace_cache/discovery.hpp" #include "core/trace_cache/post_processor.hpp" @@ -48,7 +49,7 @@ cache_manager::post_process_bulk(progress::tracker& _tracker) LOG_DEBUG("Root process ID: {}", root_pid); const auto temp_directory_content = - discovery::list_dir_files(trace_cache::tmp_directory); + discovery::list_dir_files(std::string{ trace_cache::k_tmp_directory }); LOG_TRACE("Found {} files in temp directory", temp_directory_content.size()); const auto cache_files = diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/cache_type_traits.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/cache_type_traits.hpp index 67731e28cf13..b0de50bfbcb2 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/cache_type_traits.hpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/cache_type_traits.hpp @@ -8,10 +8,9 @@ #include #include #include +#include #include -#include #include -#include #include namespace rocprofsys::trace_cache @@ -20,47 +19,6 @@ namespace rocprofsys::trace_cache namespace type_traits { -template -struct always_false : std::false_type -{}; - -} // namespace type_traits - -template -void -serialize(std::uint8_t*, const T&) -{ - static_assert(type_traits::always_false::value, "serialize not specialized"); -} - -template -T -deserialize(std::uint8_t*&) -{ - static_assert(type_traits::always_false::value, "deserialize not specialized"); - return T{}; -} - -template -size_t -get_size(const T&) -{ - static_assert(type_traits::always_false::value, "get_size(T) not specialized"); - return 0; -} - -namespace type_traits -{ - -template -struct tuple_to_variant; - -template -struct tuple_to_variant> -{ - using type = std::variant; -}; - template struct is_span : std::false_type {}; @@ -95,8 +53,8 @@ template inline constexpr bool is_array_v = is_array::value; template -static constexpr bool is_string_view_v = - std::is_same_v, std::string_view>; +concept string_like = std::is_same_v, std::string_view> || + std::is_same_v, std::string>; template struct is_optional : std::false_type @@ -112,46 +70,59 @@ inline constexpr bool is_optional_v = is_optional::value; template inline constexpr bool is_supported_type_v = is_span_v || std::is_integral_v || std::is_floating_point_v || - is_string_view_v || is_vector_v || is_optional_v || is_array_v; + string_like || is_vector_v || is_optional_v || is_array_v || + std::is_enum_v; template concept supported_cache_type = is_supported_type_v>; +template > +struct is_enum_class : std::false_type +{}; + template -struct is_enum_class -: std::bool_constant && - !std::is_convertible_v>> +struct is_enum_class +: std::bool_constant>> {}; template inline constexpr bool is_enum_class_v = is_enum_class::value; +/** + * Opt-in for a struct whose bytes may be copied as they are: trivially copyable, no + * padding, no pointers. Specialize it next to the record that uses the struct. + */ template -concept serializable = requires(std::uint8_t* dst, const T& v) { serialize(dst, v); }; +struct explicitly_raw : std::false_type +{}; +/** Types that are cached by copying their bytes: numbers, scoped enums, opted-in structs. + */ template -concept deserializable = requires(std::uint8_t*& src) { - { deserialize(src) } -> std::same_as; -}; +struct is_raw_cacheable +: std::bool_constant || is_enum_class_v || + explicitly_raw::value> +{}; -template -concept has_get_size = requires(const T& v) { - { get_size(v) } -> std::convertible_to; -}; +template +struct is_raw_cacheable> : is_raw_cacheable +{}; template concept has_type_identifier = is_enum_class_v && requires { { T::type_identifier } -> std::convertible_to; }; -template -concept cacheable = serializable && deserializable && has_get_size && - has_type_identifier; - -template -concept sample_processor = requires(T t, TypeIdentifierEnum e, const CacheableType& c) { - { t.execute_sample_processing(e, c) } -> std::same_as; +template +concept sample_handler = requires(T& handler, const SampleType& sample) { + { handler.handle_sample(sample) } -> std::same_as; }; +template +concept sample_processor = (sample_handler && ...); + +template +concept sample_visitor = (std::invocable && ...); + } // namespace type_traits } // namespace rocprofsys::trace_cache diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/cacheable.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/cacheable.hpp index 7d6fdee1251e..d8af4659cc7e 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/cacheable.hpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/cacheable.hpp @@ -2,15 +2,18 @@ // SPDX-License-Identifier: MIT #pragma once +#include "common/units/data_size.hpp" #include "core/trace_cache/cache_type_traits.hpp" #include #include +#include #include #include #include #include #include +#include #include #include @@ -19,36 +22,30 @@ using namespace std::chrono_literals; namespace rocprofsys::trace_cache { -struct cacheable_t -{ - cacheable_t() = default; -}; +using namespace common::units::literals; -constexpr size_t MByte = 1024 * 1024; -constexpr size_t buffer_size = 100 * MByte; -constexpr size_t flush_threshold = 80 * MByte; -constexpr auto CACHE_FILE_FLUSH_TIMEOUT = 10ms; +constexpr size_t k_buffer_size = static_cast((100_mib).to_bytes()); +constexpr size_t k_flush_threshold = static_cast((80_mib).to_bytes()); +constexpr auto k_cache_file_flush_timeout = 10ms; -constexpr auto ABSOLUTE = "ABS"; -constexpr auto PERCENTAGE = "%"; +constexpr auto k_absolute = "ABS"; +constexpr auto k_percentage = "%"; -template -constexpr size_t header_size = sizeof(TypeIdentifierEnum) + sizeof(size_t); -using buffer_array_t = std::array; +using buffer_array_t = std::array; -const auto tmp_directory = std::string{ "/tmp/" }; +constexpr std::string_view k_tmp_directory = "/tmp/"; namespace utility { const auto get_buffered_storage_filename = [](const int& ppid, const int& pid) { - return std::string{ tmp_directory + "buffered_storage_" + std::to_string(ppid) + "_" + - std::to_string(pid) + ".bin" }; + return std::string{ k_tmp_directory }.append( + "buffered_storage_" + std::to_string(ppid) + "_" + std::to_string(pid) + ".bin"); }; const auto get_metadata_filepath = [](const int& ppid, const int& pid) { - return std::string{ tmp_directory + "metadata_" + std::to_string(ppid) + "_" + - std::to_string(pid) + ".json" }; + return std::string{ k_tmp_directory }.append("metadata_" + std::to_string(ppid) + + "_" + std::to_string(pid) + ".json"); }; template @@ -58,15 +55,19 @@ get_size(Type&& val) { using DecayedType = std::decay_t; - if constexpr(type_traits::is_string_view_v) + if constexpr(type_traits::string_like) { return val.size() + sizeof(char) + sizeof(size_t); } else if constexpr(type_traits::is_vector_v || type_traits::is_span_v) { - using ContainerType = std::decay_t; - const size_t item_size = sizeof(typename ContainerType::value_type); + using container_type = std::decay_t; + static_assert( + type_traits::is_raw_cacheable::value, + "vectors and spans are cached as raw bytes: the element must be a " + "number, a scoped enum or a struct marked explicitly_raw"); + const size_t item_size = sizeof(typename container_type::value_type); const size_t item_count = val.size(); const size_t total_size = item_count * item_size; @@ -80,17 +81,14 @@ get_size(Type&& val) } else { + static_assert( + type_traits::is_raw_cacheable::value, + "field type is copied as raw bytes: give it serialize(Archive&) or, " + "if it is plain data, mark it explicitly_raw"); return sizeof(DecayedType); } } -template -__attribute__((always_inline)) constexpr size_t -get_size(Type&& val, Types&&... vals) -{ - return get_size(std::forward(val)) + get_size(std::forward(vals)...); -} - template requires type_traits::supported_cache_type __attribute__((always_inline)) inline void @@ -100,23 +98,34 @@ store_value(const Type& value, std::uint8_t* buffer, size_t& position) auto* dest = buffer + position; - if constexpr(type_traits::is_string_view_v) + if constexpr(type_traits::string_like) { const size_t data_size = value.size(); const size_t entry_size = sizeof(size_t) + data_size + sizeof(char); std::memcpy(dest, &data_size, sizeof(size_t)); - std::memcpy(dest + sizeof(size_t), value.data(), data_size); - dest[entry_size - sizeof(char)] = '\0'; + if(data_size != 0) + { + std::memcpy(dest + sizeof(size_t), value.data(), data_size); + } + dest[entry_size - sizeof(char)] = + '\0'; // part of the format, see binary_read_archive position += entry_size; } else if constexpr(type_traits::is_vector_v || type_traits::is_span_v) { + static_assert( + type_traits::is_raw_cacheable::value, + "vectors and spans are cached as raw bytes: the element must be a " + "number, a scoped enum or a struct marked explicitly_raw"); const size_t total_size = get_size(value); const size_t header_size = sizeof(size_t); const size_t data_size = total_size - header_size; std::memcpy(dest, &data_size, sizeof(size_t)); - std::memcpy(dest + sizeof(size_t), value.data(), data_size); + if(data_size != 0) + { + std::memcpy(dest + sizeof(size_t), value.data(), data_size); + } position += total_size; } else if constexpr(type_traits::is_optional_v) @@ -132,19 +141,15 @@ store_value(const Type& value, std::uint8_t* buffer, size_t& position) } else { + static_assert( + type_traits::is_raw_cacheable::value, + "field type is copied as raw bytes: give it serialize(Archive&) or, " + "if it is plain data, mark it explicitly_raw"); std::memcpy(dest, &value, sizeof(DecayedType)); position += sizeof(DecayedType); } } -template -__attribute__((always_inline)) inline void -store_value(std::uint8_t* buffer, const Types&... values) -{ - size_t position = 0; - (store_value(values, buffer, position), ...); -} - template requires type_traits::supported_cache_type __attribute__((always_inline)) inline static void @@ -152,7 +157,7 @@ parse_value(std::uint8_t*& data_pos, Type& arg) { using DecayedType = std::decay_t; - if constexpr(type_traits::is_string_view_v) + if constexpr(type_traits::string_like) { size_t string_size = 0; std::memcpy(&string_size, data_pos, sizeof(size_t)); @@ -163,19 +168,19 @@ parse_value(std::uint8_t*& data_pos, Type& arg) else if constexpr(type_traits::is_vector_v || type_traits::is_span_v) { - using ContainerType = std::decay_t; - using ItemType = ContainerType::value_type; - const size_t item_size = sizeof(ItemType); - size_t total_size = 0; + using item_type = std::decay_t::value_type; + static_assert(type_traits::is_raw_cacheable::value, + "vectors and spans are cached as raw bytes: the element must be a " + "number, a scoped enum or a struct marked explicitly_raw"); + size_t total_size = 0; std::memcpy(&total_size, data_pos, sizeof(size_t)); data_pos += sizeof(size_t); - const size_t item_count = total_size / item_size; - arg.reserve(item_count); - for(size_t i = 0; i < item_count; ++i) + // Only whole elements are read; a stray tail is skipped, never copied. + const size_t item_count = total_size / sizeof(item_type); + arg.resize(item_count); + if(item_count != 0) { - ItemType item; - std::memcpy(&item, data_pos + i * item_size, item_size); - arg.push_back(std::move(item)); + std::memcpy(arg.data(), data_pos, item_count * sizeof(item_type)); } data_pos += total_size; } @@ -196,18 +201,14 @@ parse_value(std::uint8_t*& data_pos, Type& arg) } else { + static_assert( + type_traits::is_raw_cacheable::value, + "field type is copied as raw bytes: give it serialize(Archive&) or, " + "if it is plain data, mark it explicitly_raw"); std::memcpy(&arg, data_pos, sizeof(DecayedType)); data_pos += sizeof(DecayedType); } } -template -__attribute__((always_inline)) inline static void -parse_value(std::uint8_t*& data_pos, Type& arg, Types&... args) -{ - parse_value(data_pos, arg); - parse_value(data_pos, args...); -} - } // namespace utility } // namespace rocprofsys::trace_cache diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/discovery.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/discovery.cpp index 2a5f27f9d550..b21112afaa2b 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/discovery.cpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/discovery.cpp @@ -93,7 +93,8 @@ find_cache_files(const pid_t& root_pid, const data::directory_files_t& dir_conte const int pid = std::stoi(match[2]); if(parent_pid == root_pid) { - cache_map[pid].buff_storage = trace_cache::tmp_directory + filename; + cache_map[pid].buff_storage = + std::string{ trace_cache::k_tmp_directory } + filename; } } else if(std::regex_match(filename, match, meta_regex)) @@ -102,7 +103,8 @@ find_cache_files(const pid_t& root_pid, const data::directory_files_t& dir_conte const int pid = std::stoi(match[2]); if(parent_pid == root_pid) { - cache_map[pid].metadata = trace_cache::tmp_directory + filename; + cache_map[pid].metadata = + std::string{ trace_cache::k_tmp_directory } + filename; } } } catch(const std::exception& e) diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/record_header.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/record_header.hpp new file mode 100644 index 000000000000..d1d3dab14b35 --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/record_header.hpp @@ -0,0 +1,46 @@ +// Copyright (c) Advanced Micro Devices, Inc. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "core/trace_cache/archive.hpp" + +#include +#include + +namespace rocprofsys::trace_cache +{ + +/** Frame in front of every record in the cache: the record type and its payload size. */ +template +struct record_header +{ + TypeIdentifierEnum type{}; + std::size_t size = 0; + + template + constexpr void serialize(Archive& archive) + { + archive(type, size); + } +}; + +template +constexpr std::size_t k_header_size = [] { + binary_size_archive archive; + record_header{}.serialize(archive); + return archive.size; +}(); + +// One fully inlined function per record type, like serialize(). +template +[[gnu::flatten]] void +write_record(std::uint8_t* buffer, record_header header, + const T& record) +{ + binary_write_archive archive{ .buffer = buffer, .position = 0 }; + header.serialize(archive); + const_cast(record).serialize(archive); +} + +} // namespace rocprofsys::trace_cache diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/sample_processor.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/sample_processor.hpp index d0522699191b..6c8fa0d661a6 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/sample_processor.hpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/sample_processor.hpp @@ -7,8 +7,6 @@ #include "core/trace_cache/sample_processor_interface.hpp" #include "core/trace_cache/sample_type.hpp" -#include - #include #include #include @@ -55,59 +53,6 @@ struct sample_processor_t return m_processors.empty(); } - ROCPROFSYS_INLINE void execute_sample_processing( - type_identifier_t type_identifier, const trace_cache::cacheable_t& sample) const - { - switch(type_identifier) - { - case type_identifier_t::region: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::kernel_dispatch: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::scratch_memory: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::memory_copy: - handle_sample(static_cast(sample)); - break; -#if ROCPROFILER_VERSION >= 600 - case type_identifier_t::memory_alloc: - handle_sample(static_cast(sample)); - break; -#endif - case type_identifier_t::in_time_sample: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::pmc_event_with_sample: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::gpu_pmc_sample: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::ainic_pmc_sample: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::cpu_pmc_sample: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::hipfile_pmc_sample: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::gpu_perf_counter_sample: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::backtrace_region_sample: - handle_sample(static_cast(sample)); - break; - case type_identifier_t::kfd_sample: - handle_sample(static_cast(sample)); - break; - default: throw std::runtime_error("Unsupported sample type"); - } - } - private: std::vector> m_processors; }; diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/sample_type.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/sample_type.hpp index 01ee4b6bf0e8..c7b1e5ef7a9e 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/sample_type.hpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/sample_type.hpp @@ -34,7 +34,7 @@ enum class type_identifier_t : std::uint32_t fragmented_space = 0xFFFF }; -struct kernel_dispatch_sample : cacheable_t +struct kernel_dispatch_sample { static constexpr type_identifier_t type_identifier = type_identifier_t::kernel_dispatch; @@ -88,52 +88,19 @@ struct kernel_dispatch_sample : cacheable_t std::uint32_t grid_size_y; std::uint32_t grid_size_z; size_t stream_handle; + + template + void serialize(Archive& archive) + { + archive(start_timestamp, end_timestamp, thread_id, agent_id_handle, kernel_id, + dispatch_id, queue_id_handle, correlation_id_internal, + correlation_id_ancestor, private_segment_size, group_segment_size, + workgroup_size_x, workgroup_size_y, workgroup_size_z, grid_size_x, + grid_size_y, grid_size_z, stream_handle); + } }; -template <> -inline void -serialize(std::uint8_t* buffer, const kernel_dispatch_sample& item) -{ - utility::store_value( - buffer, item.start_timestamp, item.end_timestamp, item.thread_id, - item.agent_id_handle, item.kernel_id, item.dispatch_id, item.queue_id_handle, - item.correlation_id_internal, item.correlation_id_ancestor, - item.private_segment_size, item.group_segment_size, item.workgroup_size_x, - item.workgroup_size_y, item.workgroup_size_z, item.grid_size_x, item.grid_size_y, - item.grid_size_z, static_cast(item.stream_handle)); -} - -template <> -inline kernel_dispatch_sample -deserialize(std::uint8_t*& buffer) -{ - kernel_dispatch_sample item; - std::uint64_t stream_handle; - utility::parse_value(buffer, item.start_timestamp, item.end_timestamp, item.thread_id, - item.agent_id_handle, item.kernel_id, item.dispatch_id, - item.queue_id_handle, item.correlation_id_internal, - item.correlation_id_ancestor, item.private_segment_size, - item.group_segment_size, item.workgroup_size_x, - item.workgroup_size_y, item.workgroup_size_z, item.grid_size_x, - item.grid_size_y, item.grid_size_z, stream_handle); - item.stream_handle = stream_handle; - return item; -} - -template <> -inline size_t -get_size(const kernel_dispatch_sample& item) -{ - return utility::get_size( - item.start_timestamp, item.end_timestamp, item.thread_id, item.agent_id_handle, - item.kernel_id, item.dispatch_id, item.queue_id_handle, - item.correlation_id_internal, item.correlation_id_ancestor, - item.private_segment_size, item.group_segment_size, item.workgroup_size_x, - item.workgroup_size_y, item.workgroup_size_z, item.grid_size_x, item.grid_size_y, - item.grid_size_z, static_cast(item.stream_handle)); -} - -struct scratch_memory_sample : cacheable_t +struct scratch_memory_sample { static constexpr type_identifier_t type_identifier = type_identifier_t::scratch_memory; @@ -172,46 +139,17 @@ struct scratch_memory_sample : cacheable_t std::uint64_t correlation_id_internal; std::uint64_t correlation_id_ancestor; size_t stream_handle; + + template + void serialize(Archive& archive) + { + archive(start_timestamp, end_timestamp, thread_id, agent_id_handle, + queue_id_handle, name, operation, flags, allocation_size, + correlation_id_internal, correlation_id_ancestor, stream_handle); + } }; -template <> -inline void -serialize(std::uint8_t* buffer, const scratch_memory_sample& item) -{ - utility::store_value(buffer, item.start_timestamp, item.end_timestamp, item.thread_id, - item.agent_id_handle, item.queue_id_handle, item.name, - item.operation, item.flags, item.allocation_size, - item.correlation_id_internal, item.correlation_id_ancestor, - static_cast(item.stream_handle)); -} - -template <> -inline scratch_memory_sample -deserialize(std::uint8_t*& buffer) -{ - scratch_memory_sample item; - std::uint64_t stream_handle; - utility::parse_value(buffer, item.start_timestamp, item.end_timestamp, item.thread_id, - item.agent_id_handle, item.queue_id_handle, item.name, - item.operation, item.flags, item.allocation_size, - item.correlation_id_internal, item.correlation_id_ancestor, - stream_handle); - item.stream_handle = stream_handle; - return item; -} - -template <> -inline size_t -get_size(const scratch_memory_sample& item) -{ - return utility::get_size(item.start_timestamp, item.end_timestamp, item.thread_id, - item.agent_id_handle, item.queue_id_handle, item.name, - item.operation, item.flags, item.allocation_size, - item.correlation_id_internal, item.correlation_id_ancestor, - static_cast(item.stream_handle)); -} - -struct memory_copy_sample : cacheable_t +struct memory_copy_sample { static constexpr type_identifier_t type_identifier = type_identifier_t::memory_copy; @@ -249,48 +187,18 @@ struct memory_copy_sample : cacheable_t std::uint64_t dst_address_value; std::uint64_t src_address_value; size_t stream_handle; + + template + void serialize(Archive& archive) + { + archive(start_timestamp, end_timestamp, thread_id, dst_agent_id_handle, + src_agent_id_handle, name, bytes, correlation_id_internal, + correlation_id_ancestor, dst_address_value, src_address_value, + stream_handle); + } }; -template <> -inline void -serialize(std::uint8_t* buffer, const memory_copy_sample& item) -{ - utility::store_value(buffer, item.start_timestamp, item.end_timestamp, item.thread_id, - item.dst_agent_id_handle, item.src_agent_id_handle, item.name, - item.bytes, item.correlation_id_internal, - item.correlation_id_ancestor, item.dst_address_value, - item.src_address_value, - static_cast(item.stream_handle)); -} - -template <> -inline memory_copy_sample -deserialize(std::uint8_t*& buffer) -{ - memory_copy_sample item; - std::uint64_t stream_handle; - utility::parse_value(buffer, item.start_timestamp, item.end_timestamp, item.thread_id, - item.dst_agent_id_handle, item.src_agent_id_handle, item.name, - item.bytes, item.correlation_id_internal, - item.correlation_id_ancestor, item.dst_address_value, - item.src_address_value, stream_handle); - item.stream_handle = stream_handle; - return item; -} - -template <> -inline size_t -get_size(const memory_copy_sample& item) -{ - return utility::get_size(item.start_timestamp, item.end_timestamp, item.thread_id, - item.dst_agent_id_handle, item.src_agent_id_handle, - item.name, item.bytes, item.correlation_id_internal, - item.correlation_id_ancestor, item.dst_address_value, - item.src_address_value, - static_cast(item.stream_handle)); -} - -struct memory_allocate_sample : cacheable_t +struct memory_allocate_sample { static constexpr type_identifier_t type_identifier = type_identifier_t::memory_alloc; @@ -326,45 +234,17 @@ struct memory_allocate_sample : cacheable_t std::uint64_t correlation_id_ancestor; std::uint64_t address_value; size_t stream_handle; + + template + void serialize(Archive& archive) + { + archive(start_timestamp, end_timestamp, thread_id, agent_id_handle, name, + operation, allocation_size, correlation_id_internal, + correlation_id_ancestor, address_value, stream_handle); + } }; -template <> -inline void -serialize(std::uint8_t* buffer, const memory_allocate_sample& item) -{ - utility::store_value(buffer, item.start_timestamp, item.end_timestamp, item.thread_id, - item.agent_id_handle, item.name, item.operation, - item.allocation_size, item.correlation_id_internal, - item.correlation_id_ancestor, item.address_value, - static_cast(item.stream_handle)); -} - -template <> -inline memory_allocate_sample -deserialize(std::uint8_t*& buffer) -{ - memory_allocate_sample item; - std::uint64_t stream_handle; - utility::parse_value(buffer, item.start_timestamp, item.end_timestamp, item.thread_id, - item.agent_id_handle, item.name, item.operation, - item.allocation_size, item.correlation_id_internal, - item.correlation_id_ancestor, item.address_value, stream_handle); - item.stream_handle = stream_handle; - return item; -} - -template <> -inline size_t -get_size(const memory_allocate_sample& item) -{ - return utility::get_size(item.start_timestamp, item.end_timestamp, item.thread_id, - item.agent_id_handle, item.name, item.operation, - item.allocation_size, item.correlation_id_internal, - item.correlation_id_ancestor, item.address_value, - static_cast(item.stream_handle)); -} - -struct region_sample : cacheable_t +struct region_sample { static constexpr type_identifier_t type_identifier = type_identifier_t::region; @@ -395,41 +275,16 @@ struct region_sample : cacheable_t std::string_view call_stack; std::string_view args_str; std::string_view category; -}; -template <> -inline void -serialize(std::uint8_t* buffer, const region_sample& item) -{ - utility::store_value(buffer, item.thread_id, item.name, item.correlation_id_internal, - item.correlation_id_ancestor, item.start_timestamp, - item.end_timestamp, item.call_stack, item.args_str, - item.category); -} - -template <> -inline region_sample -deserialize(std::uint8_t*& buffer) -{ - region_sample item; - utility::parse_value(buffer, item.thread_id, item.name, item.correlation_id_internal, - item.correlation_id_ancestor, item.start_timestamp, - item.end_timestamp, item.call_stack, item.args_str, - item.category); - return item; -} - -template <> -inline size_t -get_size(const region_sample& item) -{ - return utility::get_size(item.thread_id, item.name, item.correlation_id_internal, - item.correlation_id_ancestor, item.start_timestamp, - item.end_timestamp, item.call_stack, item.args_str, - item.category); -} + template + void serialize(Archive& archive) + { + archive(thread_id, name, correlation_id_internal, correlation_id_ancestor, + start_timestamp, end_timestamp, call_stack, args_str, category); + } +}; -struct in_time_sample : cacheable_t +struct in_time_sample { static constexpr type_identifier_t type_identifier = type_identifier_t::in_time_sample; @@ -460,49 +315,14 @@ struct in_time_sample : cacheable_t size_t correlation_id; std::string_view call_stack; std::string_view line_info; -}; -template <> -inline void -serialize(std::uint8_t* buffer, const in_time_sample& item) -{ - utility::store_value(buffer, item.category_enum_id, item.track_name, - static_cast(item.timestamp_ns), - item.event_metadata, static_cast(item.stack_id), - static_cast(item.parent_stack_id), - static_cast(item.correlation_id), item.call_stack, - item.line_info); -} - -template <> -inline in_time_sample -deserialize(std::uint8_t*& buffer) -{ - in_time_sample item; - size_t category_enum_id; - std::uint64_t timestamp_ns, stack_id, parent_stack_id, correlation_id; - utility::parse_value(buffer, category_enum_id, item.track_name, timestamp_ns, - item.event_metadata, stack_id, parent_stack_id, correlation_id, - item.call_stack, item.line_info); - item.category_enum_id = category_enum_id; - item.timestamp_ns = timestamp_ns; - item.stack_id = stack_id; - item.parent_stack_id = parent_stack_id; - item.correlation_id = correlation_id; - return item; -} - -template <> -inline size_t -get_size(const in_time_sample& item) -{ - return utility::get_size( - item.category_enum_id, item.track_name, - static_cast(item.timestamp_ns), item.event_metadata, - static_cast(item.stack_id), - static_cast(item.parent_stack_id), - static_cast(item.correlation_id), item.call_stack, item.line_info); -} + template + void serialize(Archive& archive) + { + archive(category_enum_id, track_name, timestamp_ns, event_metadata, stack_id, + parent_stack_id, correlation_id, call_stack, line_info); + } +}; struct pmc_event_with_sample : in_time_sample { @@ -533,56 +353,16 @@ struct pmc_event_with_sample : in_time_sample std::string_view pmc_info_name; double value; std::optional system_tid; + + template + void serialize(Archive& archive) + { + in_time_sample::serialize(archive); + archive(device_id, device_type, pmc_info_name, value, system_tid); + } }; -template <> -inline void -serialize(std::uint8_t* buffer, const pmc_event_with_sample& item) -{ - utility::store_value(buffer, item.category_enum_id, item.track_name, - static_cast(item.timestamp_ns), - item.event_metadata, static_cast(item.stack_id), - static_cast(item.parent_stack_id), - static_cast(item.correlation_id), item.call_stack, - item.line_info, item.device_id, item.device_type, - item.pmc_info_name, item.value, item.system_tid); -} - -template <> -inline pmc_event_with_sample -deserialize(std::uint8_t*& buffer) -{ - pmc_event_with_sample item; - size_t category_enum_id; - std::uint64_t timestamp_ns, stack_id, parent_stack_id, correlation_id; - utility::parse_value(buffer, category_enum_id, item.track_name, timestamp_ns, - item.event_metadata, stack_id, parent_stack_id, correlation_id, - item.call_stack, item.line_info, item.device_id, - item.device_type, item.pmc_info_name, item.value, - item.system_tid); - item.category_enum_id = category_enum_id; - item.timestamp_ns = timestamp_ns; - item.stack_id = stack_id; - item.parent_stack_id = parent_stack_id; - item.correlation_id = correlation_id; - return item; -} - -template <> -inline size_t -get_size(const pmc_event_with_sample& item) -{ - return utility::get_size( - item.category_enum_id, item.track_name, - static_cast(item.timestamp_ns), item.event_metadata, - static_cast(item.stack_id), - static_cast(item.parent_stack_id), - static_cast(item.correlation_id), item.call_stack, item.line_info, - item.device_id, item.device_type, item.pmc_info_name, item.value, - item.system_tid); -} - -struct backtrace_region_sample : cacheable_t +struct backtrace_region_sample { static constexpr type_identifier_t type_identifier = type_identifier_t::backtrace_region_sample; @@ -616,38 +396,16 @@ struct backtrace_region_sample : cacheable_t std::string_view call_stack; std::string_view line_info; std::string_view extdata; -}; -template <> -inline void -serialize(std::uint8_t* buffer, const backtrace_region_sample& item) -{ - utility::store_value(buffer, item.type, item.thread_id, item.track_name, item.name, - item.start_timestamp, item.end_timestamp, item.category, - item.call_stack, item.line_info, item.extdata); -} - -template <> -inline backtrace_region_sample -deserialize(std::uint8_t*& buffer) -{ - backtrace_region_sample item; - utility::parse_value(buffer, item.type, item.thread_id, item.track_name, item.name, - item.start_timestamp, item.end_timestamp, item.category, - item.call_stack, item.line_info, item.extdata); - return item; -} - -template <> -inline size_t -get_size(const backtrace_region_sample& item) -{ - return utility::get_size(item.type, item.thread_id, item.track_name, item.name, - item.start_timestamp, item.end_timestamp, item.category, - item.call_stack, item.line_info, item.extdata); -} + template + void serialize(Archive& archive) + { + archive(type, thread_id, track_name, name, start_timestamp, end_timestamp, + category, call_stack, line_info, extdata); + } +}; -struct kfd_sample : cacheable_t +struct kfd_sample { static constexpr type_identifier_t type_identifier = type_identifier_t::kfd_sample; @@ -688,41 +446,14 @@ struct kfd_sample : cacheable_t std::string_view pmc_info_name; double value; std::optional system_tid; -}; -template <> -inline void -serialize(std::uint8_t* buffer, const kfd_sample& item) -{ - utility::store_value(buffer, item.thread_id, item.name, item.start_timestamp, - item.end_timestamp, item.args_str, item.category, - item.track_name, item.event_metadata, item.device_id, - item.device_type, item.pmc_info_name, item.value, - item.system_tid); -} - -template <> -inline kfd_sample -deserialize(std::uint8_t*& buffer) -{ - kfd_sample item; - utility::parse_value(buffer, item.thread_id, item.name, item.start_timestamp, - item.end_timestamp, item.args_str, item.category, - item.track_name, item.event_metadata, item.device_id, - item.device_type, item.pmc_info_name, item.value, - item.system_tid); - return item; -} - -template <> -inline size_t -get_size(const kfd_sample& item) -{ - return utility::get_size(item.thread_id, item.name, item.start_timestamp, - item.end_timestamp, item.args_str, item.category, - item.track_name, item.event_metadata, item.device_id, - item.device_type, item.pmc_info_name, item.value, - item.system_tid); -} + template + void serialize(Archive& archive) + { + archive(thread_id, name, start_timestamp, end_timestamp, args_str, category, + track_name, event_metadata, device_id, device_type, pmc_info_name, value, + system_tid); + } +}; } // namespace rocprofsys::trace_cache diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/storage_parser.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/storage_parser.hpp index ab5cbbc223dd..a4fab0041df4 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/storage_parser.hpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/storage_parser.hpp @@ -6,12 +6,15 @@ #include "common/defines.h" #include "core/progress/callback.hpp" +#include "core/trace_cache/archive.hpp" #include "core/trace_cache/cache_type_traits.hpp" #include "core/trace_cache/cacheable.hpp" +#include "core/trace_cache/record_header.hpp" #include "core/trace_cache/type_registry.hpp" #include "logger/debug.hpp" +#include #include #include #include @@ -37,8 +40,7 @@ class storage_parser {} template - requires type_traits::sample_processor + requires type_traits::sample_processor void load(std::shared_ptr _type_processing, progress::progress_callback _progress_cb = {}) { @@ -56,13 +58,8 @@ class storage_parser fmt::format("Error opening file for reading: {}", m_filename)); } - struct __attribute__((packed)) sample_header - { - TypeIdentifierEnum type; - size_t sample_size; - }; - - sample_header header; + std::array> header_bytes{}; + record_header header; std::vector sample; sample.reserve(4096); @@ -78,21 +75,23 @@ class storage_parser "Stream not in good state, stopping parse. File: {}", m_filename)); } - ifs.read(reinterpret_cast(&header), sizeof(header)); + ifs.read(reinterpret_cast(header_bytes.data()), header_bytes.size()); + binary_read_archive header_reader{ .cursor = header_bytes.data() }; + header_reader(header); - if(header.sample_size == 0 || ifs.eof()) + if(header.size == 0 || ifs.eof()) { continue; } - if(ROCPROFSYS_UNLIKELY(header.sample_size > last_capacity)) + if(ROCPROFSYS_UNLIKELY(header.size > last_capacity)) { - sample.reserve(header.sample_size); + sample.reserve(header.size); last_capacity = sample.capacity(); } - sample.resize(header.sample_size); - ifs.read(reinterpret_cast(sample.data()), header.sample_size); + sample.resize(header.size); + ifs.read(reinterpret_cast(sample.data()), header.size); if(ifs.fail()) { @@ -124,21 +123,9 @@ class storage_parser auto* data = sample.data(); - auto sample_value = m_registry.get_type(header.type, data); - if(sample_value.has_value()) - { - _type_processing->execute_sample_processing( - header.type, std::visit( - [](auto& arg) -> cacheable_t& { - return static_cast(arg); - }, - sample_value.value())); - } - else - { - LOG_TRACE("Unknown sample type encountered, skipping"); - continue; - } + m_registry.dispatch(header.type, data, [&](const auto& record) { + _type_processing->handle_sample(record); + }); } ifs.close(); diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/CMakeLists.txt b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/CMakeLists.txt index ae79bb1fe907..7e57f327fd64 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/CMakeLists.txt +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/CMakeLists.txt @@ -3,6 +3,7 @@ set(trace_cache_tests_sources test_agent_info_json.cpp + test_archive.cpp test_args_serialization.cpp test_buffer_storage.cpp test_cache_integration.cpp @@ -13,6 +14,7 @@ set(trace_cache_tests_sources test_kfd_args.cpp test_metadata_registry_json.cpp test_metadata_registry.cpp + test_record_format.cpp test_rocpd_pmc_name_contract.cpp test_rocpd_processor.cpp test_rocpd_xcp_output.cpp diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/mocked_types.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/mocked_types.hpp index 78197dd3c80a..eb57c2f2aaac 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/mocked_types.hpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/mocked_types.hpp @@ -19,7 +19,7 @@ enum class test_type_identifier_t : std::uint32_t sample_type_5 = 5, fragmented_space = 0xFFFF }; -struct test_sample_1 : public rocprofsys::trace_cache::cacheable_t +struct test_sample_1 { static constexpr test_type_identifier_t type_identifier = test_type_identifier_t::sample_type_1; @@ -37,9 +37,15 @@ struct test_sample_1 : public rocprofsys::trace_cache::cacheable_t { return value == other.value && text == other.text; } + + template + void serialize(Archive& archive) + { + archive(value, text); + } }; -struct test_sample_2 : public rocprofsys::trace_cache::cacheable_t +struct test_sample_2 { static constexpr test_type_identifier_t type_identifier = test_type_identifier_t::sample_type_2; @@ -72,9 +78,15 @@ struct test_sample_2 : public rocprofsys::trace_cache::cacheable_t return std::abs(data - other.data) < 1e-9; } + + template + void serialize(Archive& archive) + { + archive(data, sample_id); + } }; -struct test_sample_3 : public rocprofsys::trace_cache::cacheable_t +struct test_sample_3 { static constexpr test_type_identifier_t type_identifier = test_type_identifier_t::sample_type_3; @@ -87,9 +99,15 @@ struct test_sample_3 : public rocprofsys::trace_cache::cacheable_t std::vector payload; bool operator==(const test_sample_3& other) const { return payload == other.payload; } + + template + void serialize(Archive& archive) + { + archive(payload); + } }; -struct test_sample_4 : public rocprofsys::trace_cache::cacheable_t +struct test_sample_4 { static constexpr test_type_identifier_t type_identifier = test_type_identifier_t::sample_type_4; @@ -102,9 +120,15 @@ struct test_sample_4 : public rocprofsys::trace_cache::cacheable_t std::vector data; bool operator==(const test_sample_4& other) const { return data == other.data; } + + template + void serialize(Archive& archive) + { + archive(data); + } }; -struct test_sample_5 : public rocprofsys::trace_cache::cacheable_t +struct test_sample_5 { static constexpr test_type_identifier_t type_identifier = test_type_identifier_t::sample_type_5; @@ -117,119 +141,10 @@ struct test_sample_5 : public rocprofsys::trace_cache::cacheable_t std::optional data; bool operator==(const test_sample_5& other) const { return data == other.data; } -}; - -template <> -inline void -rocprofsys::trace_cache::serialize(std::uint8_t* buffer, const test_sample_1& item) -{ - rocprofsys::trace_cache::utility::store_value(buffer, item.value, item.text); -} - -template <> -inline test_sample_1 -rocprofsys::trace_cache::deserialize(std::uint8_t*& buffer) -{ - test_sample_1 result; - rocprofsys::trace_cache::utility::parse_value(buffer, result.value, result.text); - return result; -} - -template <> -inline size_t -rocprofsys::trace_cache::get_size(const test_sample_1& item) -{ - return rocprofsys::trace_cache::utility::get_size(item.value, item.text); -} - -template <> -inline void -rocprofsys::trace_cache::serialize(std::uint8_t* buffer, const test_sample_2& item) -{ - rocprofsys::trace_cache::utility::store_value(buffer, item.data, item.sample_id); -} -template <> -inline test_sample_2 -rocprofsys::trace_cache::deserialize(std::uint8_t*& buffer) -{ - test_sample_2 result; - rocprofsys::trace_cache::utility::parse_value(buffer, result.data, result.sample_id); - return result; -} - -template <> -inline size_t -rocprofsys::trace_cache::get_size(const test_sample_2& item) -{ - return rocprofsys::trace_cache::utility::get_size(item.data, item.sample_id); -} - -template <> -inline void -rocprofsys::trace_cache::serialize(std::uint8_t* buffer, const test_sample_3& item) -{ - rocprofsys::trace_cache::utility::store_value(buffer, item.payload); -} - -template <> -inline test_sample_3 -rocprofsys::trace_cache::deserialize(std::uint8_t*& buffer) -{ - test_sample_3 result; - rocprofsys::trace_cache::utility::parse_value(buffer, result.payload); - return result; -} - -template <> -inline size_t -rocprofsys::trace_cache::get_size(const test_sample_3& item) -{ - return rocprofsys::trace_cache::utility::get_size(item.payload); -} - -template <> -inline void -rocprofsys::trace_cache::serialize(std::uint8_t* buffer, const test_sample_4& item) -{ - rocprofsys::trace_cache::utility::store_value(buffer, item.data); -} - -template <> -inline test_sample_4 -rocprofsys::trace_cache::deserialize(std::uint8_t*& buffer) -{ - test_sample_4 result; - rocprofsys::trace_cache::utility::parse_value(buffer, result.data); - return result; -} - -template <> -inline size_t -rocprofsys::trace_cache::get_size(const test_sample_4& item) -{ - return rocprofsys::trace_cache::utility::get_size(item.data); -} - -template <> -inline void -rocprofsys::trace_cache::serialize(std::uint8_t* buffer, const test_sample_5& item) -{ - rocprofsys::trace_cache::utility::store_value(buffer, item.data); -} - -template <> -inline test_sample_5 -rocprofsys::trace_cache::deserialize(std::uint8_t*& buffer) -{ - test_sample_5 result; - rocprofsys::trace_cache::utility::parse_value(buffer, result.data); - return result; -} - -template <> -inline size_t -rocprofsys::trace_cache::get_size(const test_sample_5& item) -{ - return rocprofsys::trace_cache::utility::get_size(item.data); -} + template + void serialize(Archive& archive) + { + archive(data); + } +}; diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_archive.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_archive.cpp new file mode 100644 index 000000000000..720d504f7330 --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_archive.cpp @@ -0,0 +1,351 @@ +// Copyright (c) Advanced Micro Devices, Inc. +// SPDX-License-Identifier: MIT + +// NOLINTBEGIN(readability-magic-numbers, readability-identifier-naming) +// Test values in this file are arbitrary and chosen for coverage, not domain significance + +#include "core/trace_cache/archive.hpp" +#include "core/trace_cache/cacheable.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rocprofsys::trace_cache +{ + +namespace +{ + +enum class test_record_id : std::uint32_t +{ + scalar_test = 1, + string_test = 2, + vector_test = 3, + optional_test = 4, + derived_test = 6, + nested_test = 8, +}; + +// A record counts as serializable only when it declares its own serialize(Archive&). +struct base_with_serialize +{ + std::uint32_t first = 0; + + template + void serialize(Archive& archive) + { + archive(first); + } +}; + +struct derived_without_serialize : base_with_serialize +{ + std::uint32_t second = 0; +}; + +struct serialize_takes_archive_by_value +{ + std::uint32_t first = 0; + + template + void serialize(Archive archive) + { + archive(first); + } +}; + +static_assert(archive_serializable); +static_assert(archive_serializable); +static_assert(archive_serializable); +static_assert(!archive_serializable, + "an inherited serialize would skip the fields of the derived record"); +static_assert( + !archive_serializable, + "a serialize that takes the archive by value loses the position"); +static_assert(!archive_serializable); + +struct scalar_record +{ + static constexpr auto type_identifier = test_record_id::scalar_test; + + std::uint32_t val_u32 = 0; + std::uint64_t val_u64 = 0; + double val_dbl = 0.0; + + template + void serialize(Archive& archive) + { + archive(val_u32, val_u64, val_dbl); + } +}; + +struct string_record +{ + static constexpr auto type_identifier = test_record_id::string_test; + + std::string_view text; + + template + void serialize(Archive& archive) + { + archive(text); + } +}; + +struct vector_record +{ + static constexpr auto type_identifier = test_record_id::vector_test; + + std::vector data; + + template + void serialize(Archive& archive) + { + archive(data); + } +}; + +struct optional_record +{ + static constexpr auto type_identifier = test_record_id::optional_test; + + std::optional value; + + template + void serialize(Archive& archive) + { + archive(value); + } +}; + +struct base_record +{ + static constexpr auto type_identifier = test_record_id::derived_test; + + std::uint32_t base_field = 0; + double base_value = 0.0; + + template + void serialize(Archive& archive) + { + archive(base_field, base_value); + } +}; + +struct derived_record : base_record +{ + static constexpr auto type_identifier = test_record_id::derived_test; + + std::uint64_t extra_field = 0; + + template + void serialize(Archive& archive) + { + base_record::serialize(archive); + archive(extra_field); + } +}; + +struct inner_values +{ + std::uint32_t alpha = 0; + double beta = 0.0; + + template + void serialize(Archive& archive) + { + archive(alpha, beta); + } +}; + +struct nested_record +{ + static constexpr auto type_identifier = test_record_id::nested_test; + + std::uint64_t head = 0; + inner_values inner; + std::uint16_t tail = 0; + + template + void serialize(Archive& archive) + { + archive(head, inner, tail); + } +}; + +} // namespace + +constexpr std::size_t k_test_buffer_size = 4096; + +TEST(archive, scalars_match_utility_encoding) +{ + std::array buffer{}; + const std::uint32_t val_u32 = 12345U; + const std::uint64_t val_u64 = 98765432101234ULL; + const double val_dbl = 2.71828; + + scalar_record orig; + orig.val_u32 = val_u32; + orig.val_u64 = val_u64; + orig.val_dbl = val_dbl; + + const std::size_t size = get_size(orig); + serialize(buffer.data(), orig); + + std::uint8_t* read_buffer = buffer.data(); + const scalar_record read = deserialize(read_buffer); + + EXPECT_EQ(read.val_u32, val_u32); + EXPECT_EQ(read.val_u64, val_u64); + EXPECT_DOUBLE_EQ(read.val_dbl, val_dbl); + EXPECT_EQ(read_buffer - buffer.data(), static_cast(size)); + + EXPECT_EQ(size, utility::get_size(val_u32) + utility::get_size(val_u64) + + utility::get_size(val_dbl)); + + std::array other_buffer{}; + std::size_t utility_position = 0; + utility::store_value(val_u32, other_buffer.data(), utility_position); + utility::store_value(val_u64, other_buffer.data(), utility_position); + utility::store_value(val_dbl, other_buffer.data(), utility_position); + + EXPECT_EQ(std::memcmp(buffer.data(), other_buffer.data(), size), 0); +} + +TEST(archive, string_view_reads_back_as_view_into_buffer) +{ + std::array buffer{}; + const std::string_view orig_text = "hello world"; + + string_record orig; + orig.text = orig_text; + + const std::size_t size = get_size(orig); + serialize(buffer.data(), orig); + + std::uint8_t* read_buffer = buffer.data(); + const string_record read = deserialize(read_buffer); + + EXPECT_EQ(read.text, orig_text); + EXPECT_EQ(read.text.data(), + reinterpret_cast(buffer.data() + sizeof(std::size_t))); + EXPECT_EQ(read_buffer - buffer.data(), static_cast(size)); +} + +TEST(archive, vector_round_trip) +{ + std::array buffer{}; + const std::vector orig_data = { 10, 20, 30 }; + + vector_record orig; + orig.data = orig_data; + + const std::size_t size = get_size(orig); + serialize(buffer.data(), orig); + + std::uint8_t* read_buffer = buffer.data(); + const vector_record read = deserialize(read_buffer); + + EXPECT_EQ(read.data, orig_data); + EXPECT_EQ(read_buffer - buffer.data(), static_cast(size)); +} + +TEST(archive, optional_engaged_and_empty) +{ + std::array buffer{}; + optional_record orig_engaged; + orig_engaged.value = 9876543210LL; + + const std::size_t size_engaged = get_size(orig_engaged); + serialize(buffer.data(), orig_engaged); + + std::uint8_t* read_buf = buffer.data(); + const optional_record read_engaged = deserialize(read_buf); + + ASSERT_TRUE(read_engaged.value.has_value()); + EXPECT_EQ(read_engaged.value, orig_engaged.value); + + buffer.fill(0); + optional_record orig_empty; + orig_empty.value.reset(); + + const std::size_t size_empty = get_size(orig_empty); + serialize(buffer.data(), orig_empty); + + read_buf = buffer.data(); + const optional_record read_empty = deserialize(read_buf); + + EXPECT_FALSE(read_empty.value.has_value()); + + EXPECT_EQ(size_engaged, size_empty + sizeof(std::int64_t)); +} + +TEST(archive, derived_record_serializes_base_first) +{ + std::array buffer{}; + derived_record orig; + orig.base_field = 555U; + orig.base_value = 3.14; + orig.extra_field = 777ULL; + + const std::size_t base_size = + get_size(base_record{ .base_field = 555U, .base_value = 3.14 }); + const std::size_t total_size = get_size(orig); + + serialize(buffer.data(), orig); + + std::uint8_t* read_buffer = buffer.data(); + const derived_record read = deserialize(read_buffer); + + EXPECT_EQ(read.base_field, 555U); + EXPECT_DOUBLE_EQ(read.base_value, 3.14); + EXPECT_EQ(read.extra_field, 777ULL); + + EXPECT_EQ(total_size, base_size + sizeof(std::uint64_t)); + EXPECT_EQ(read_buffer - buffer.data(), static_cast(total_size)); +} + +TEST(archive, nested_struct_serializes_itself) +{ + std::array buffer{}; + nested_record orig; + orig.head = 1111ULL; + orig.inner.alpha = 2222U; + orig.inner.beta = 3.33; + orig.tail = 4444U; + + const std::size_t size = get_size(orig); + serialize(buffer.data(), orig); + + std::uint8_t* read_buffer = buffer.data(); + const nested_record read = deserialize(read_buffer); + + EXPECT_EQ(read.head, 1111ULL); + EXPECT_EQ(read.inner.alpha, 2222U); + EXPECT_DOUBLE_EQ(read.inner.beta, 3.33); + EXPECT_EQ(read.tail, 4444U); + EXPECT_EQ(read_buffer - buffer.data(), static_cast(size)); + + EXPECT_EQ(size, utility::get_size(orig.head) + utility::get_size(orig.inner.alpha) + + utility::get_size(orig.inner.beta) + + utility::get_size(orig.tail)); + + std::array other_buffer{}; + std::size_t nested_position = 0; + utility::store_value(orig.head, other_buffer.data(), nested_position); + utility::store_value(orig.inner.alpha, other_buffer.data(), nested_position); + utility::store_value(orig.inner.beta, other_buffer.data(), nested_position); + utility::store_value(orig.tail, other_buffer.data(), nested_position); + + EXPECT_EQ(std::memcmp(buffer.data(), other_buffer.data(), size), 0); +} + +} // namespace rocprofsys::trace_cache + +// NOLINTEND(readability-magic-numbers, readability-identifier-naming) diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_buffer_storage.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_buffer_storage.cpp index d5c3df44515c..06d94cc5fb2a 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_buffer_storage.cpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_buffer_storage.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT #include "core/trace_cache/buffer_storage.hpp" +#include "core/trace_cache/cacheable.hpp" #include "mocked_types.hpp" #include @@ -479,7 +480,7 @@ TEST_F(buffer_storage_test, repeated_fragmentation) storage.start(); - const size_t fragment_trigger_size = rocprofsys::trace_cache::buffer_size / 5; + const size_t fragment_trigger_size = rocprofsys::trace_cache::k_buffer_size / 5; const std::vector fragment_payload(fragment_trigger_size, 0xDD); const int cycle_count = 3; const int iter_count = 2; diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_cache_integration.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_cache_integration.cpp index 2352b1f9c5b1..5dadbb7280d3 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_cache_integration.cpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_cache_integration.cpp @@ -140,53 +140,39 @@ class integration_sample_processor_t } } - void execute_sample_processing(test_type_identifier_t type_identifier, - const rocprofsys::trace_cache::cacheable_t& value) + void handle_sample(const test_sample_1& sample) { - switch(type_identifier) - { - case test_type_identifier_t::sample_type_1: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - m_sample_1_count++; - check_sample_1(sample); - break; - } - case test_type_identifier_t::sample_type_2: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - m_sample_2_count++; - check_sample_2(sample); - break; - } - case test_type_identifier_t::sample_type_3: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - m_sample_3_count++; - check_sample_3(sample); - break; - } - case test_type_identifier_t::sample_type_4: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - m_sample_4_count++; - check_sample_4(sample); - break; - } - case test_type_identifier_t::sample_type_5: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - m_sample_5_count++; - check_sample_5(sample); - break; - } - default: break; - } + const std::lock_guard lock(m_data_mutex); + m_sample_1_count++; + check_sample_1(sample); + } + + void handle_sample(const test_sample_2& sample) + { + const std::lock_guard lock(m_data_mutex); + m_sample_2_count++; + check_sample_2(sample); + } + + void handle_sample(const test_sample_3& sample) + { + const std::lock_guard lock(m_data_mutex); + m_sample_3_count++; + check_sample_3(sample); + } + + void handle_sample(const test_sample_4& sample) + { + const std::lock_guard lock(m_data_mutex); + m_sample_4_count++; + check_sample_4(sample); + } + + void handle_sample(const test_sample_5& sample) + { + const std::lock_guard lock(m_data_mutex); + m_sample_5_count++; + check_sample_5(sample); } int get_sample_1_count() const { return m_sample_1_count.load(); } diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_cacheable.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_cacheable.cpp index 122cc7fa0a39..519e9d99f293 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_cacheable.cpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_cacheable.cpp @@ -7,13 +7,57 @@ #include #include #include +#include #include +#include #include #include +#include +#include #include using namespace std::string_view_literals; +namespace type_traits = rocprofsys::trace_cache::type_traits; + +// Fields that are copied as raw bytes must be plain data. +enum class scoped_test_enum : std::uint8_t +{ + first +}; +struct plain_test_struct +{ + std::uint64_t id = 0; + double value = 0.0; +}; +struct marked_test_struct +{ + std::uint64_t id = 0; + double value = 0.0; +}; + +template <> +struct rocprofsys::trace_cache::type_traits::explicitly_raw +: std::true_type +{}; + +static_assert(type_traits::is_raw_cacheable::value); +static_assert(type_traits::is_raw_cacheable::value); +static_assert(type_traits::is_raw_cacheable::value); +static_assert(type_traits::is_raw_cacheable>::value); +static_assert(type_traits::is_raw_cacheable::value); +static_assert(type_traits::is_raw_cacheable>::value); +static_assert(!type_traits::is_raw_cacheable::value, + "a struct must opt in: padding or a pointer would be copied silently"); +static_assert(!type_traits::is_raw_cacheable::value, + "an unscoped enum has no fixed underlying type"); +static_assert(!type_traits::is_raw_cacheable::value); +static_assert(!type_traits::is_raw_cacheable::value); +static_assert(!type_traits::is_raw_cacheable>::value); +static_assert(!type_traits::is_raw_cacheable>::value, + "views point into memory that is not cached"); +static_assert(!type_traits::is_raw_cacheable>::value); + class cacheable_test : public ::testing::Test { protected: @@ -343,8 +387,10 @@ TEST_F(cacheable_test, parse_multiple_values_with_optional) std::string_view parsed_str; std::optional parsed_opt_empty; - rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_int, parsed_opt, - parsed_str, parsed_opt_empty); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_int); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_opt); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_str); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_opt_empty); EXPECT_EQ(parsed_int, 100); ASSERT_TRUE(parsed_opt.has_value()); @@ -495,8 +541,11 @@ TEST_F(cacheable_test, parse_multiple_values_with_optional_vector_and_string_vie std::optional> parsed_nullvec; std::optional parsed_nullstr; - rocprofsys::trace_cache::utility::parse_value( - data_pos, parsed_int, parsed_vec, parsed_str, parsed_nullvec, parsed_nullstr); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_int); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_vec); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_str); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_nullvec); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_nullstr); EXPECT_EQ(parsed_int, 42); ASSERT_TRUE(parsed_vec.has_value()); @@ -536,6 +585,27 @@ TEST_F(cacheable_test, parse_value_empty_byte_array) EXPECT_TRUE(parsed_value.empty()); EXPECT_EQ(data_pos, buffer.data() + sizeof(size_t)); } +TEST_F(cacheable_test, parse_value_vector_skips_a_trailing_partial_element) +{ + // The byte count is one whole std::uint32_t plus one stray byte. Only whole elements + // are read; the cursor still moves past the whole count. + constexpr std::uint64_t k_byte_count = sizeof(std::uint32_t) + 1; + constexpr std::uint32_t k_whole = 0xDEADBEEF; + constexpr std::uint8_t k_stray_byte = 0xAA; + + std::memcpy(buffer.data(), &k_byte_count, sizeof(k_byte_count)); + std::memcpy(buffer.data() + sizeof(k_byte_count), &k_whole, sizeof(k_whole)); + buffer[sizeof(k_byte_count) + sizeof(k_whole)] = k_stray_byte; + + std::uint8_t* data_pos = buffer.data(); + std::vector parsed; + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed); + + ASSERT_EQ(parsed.size(), 1U); + EXPECT_EQ(parsed.front(), k_whole); + EXPECT_EQ(data_pos, buffer.data() + sizeof(k_byte_count) + k_byte_count); +} + TEST_F(cacheable_test, parse_multiple_values) { const int int_val = 42; @@ -555,8 +625,10 @@ TEST_F(cacheable_test, parse_multiple_values) std::string_view parsed_string; unsigned char parsed_uchar; - rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_int, parsed_double, - parsed_string, parsed_uchar); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_int); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_double); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_string); + rocprofsys::trace_cache::utility::parse_value(data_pos, parsed_uchar); EXPECT_EQ(parsed_int, 42); EXPECT_DOUBLE_EQ(parsed_double, 3.14); @@ -912,10 +984,16 @@ TEST(type_traits_test, is_supported_type_v) EXPECT_TRUE( rocprofsys::trace_cache::type_traits::is_supported_type_v>); + EXPECT_TRUE(rocprofsys::trace_cache::type_traits::is_supported_type_v); + + enum class custom_enum : std::uint16_t + { + }; + EXPECT_TRUE(rocprofsys::trace_cache::type_traits::is_supported_type_v); + struct custom_type {}; EXPECT_FALSE(rocprofsys::trace_cache::type_traits::is_supported_type_v); - EXPECT_FALSE(rocprofsys::trace_cache::type_traits::is_supported_type_v); } TEST_F(cacheable_test, get_buffered_storage_filename) diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_record_format.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_record_format.cpp new file mode 100644 index 000000000000..0f9daf94657f --- /dev/null +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_record_format.cpp @@ -0,0 +1,692 @@ +// Copyright (c) Advanced Micro Devices, Inc. +// SPDX-License-Identifier: MIT + +#include "backends/amd_smi/gpu_types.hpp" +#include "core/trace_cache/archive.hpp" +#include "core/trace_cache/record_header.hpp" +#include "core/trace_cache/sample_type.hpp" +#include "library/pmc/collectors/cpu/sample.hpp" +#include "library/pmc/collectors/cpu/types.hpp" +#include "library/pmc/collectors/gpu/sample.hpp" +#include "library/pmc/collectors/gpu/types.hpp" +#include "library/pmc/collectors/gpu_perf_counter/sample.hpp" +#include "library/pmc/collectors/gpu_perf_counter/types.hpp" +#include "library/pmc/collectors/hipfile/sample.hpp" +#include "library/pmc/collectors/hipfile/types.hpp" +#include "library/pmc/collectors/nic/sample.hpp" +#include "library/pmc/collectors/nic/types.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace rocprofsys::trace_cache; + +// NOLINTBEGIN(readability-magic-numbers) +// Golden test values and bytes are arbitrary by design. +namespace +{ + +template +void +expect_golden(const Record& record, const std::array& expected) +{ + ASSERT_EQ(get_size(record), N); + + std::array buffer = {}; + serialize(buffer.data(), record); + EXPECT_EQ(buffer, expected); + + std::array writable_copy = expected; + std::uint8_t* cursor = writable_copy.data(); + const Record deserialized = deserialize(cursor); + const std::size_t bytes_read = cursor - writable_copy.data(); + EXPECT_EQ(bytes_read, N); + + std::array reserialized = {}; + serialize(reserialized.data(), deserialized); + EXPECT_EQ(reserialized, expected); +} + +} // namespace + +TEST(record_format, record_header) +{ + const rocprofsys::trace_cache::record_header< + rocprofsys::trace_cache::type_identifier_t> + header{ .type = rocprofsys::trace_cache::type_identifier_t::kernel_dispatch, + .size = 0x1122334455667788UL }; + + constexpr std::array k_expected{ + 0x03, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 + }; + static_assert(rocprofsys::trace_cache::k_header_size< + rocprofsys::trace_cache::type_identifier_t> == k_expected.size()); + + std::array buffer{}; + rocprofsys::trace_cache::binary_write_archive archive{ .buffer = buffer.data(), + .position = 0 }; + archive(header); + + EXPECT_EQ(buffer, k_expected); +} + +TEST(record_format, kernel_dispatch_sample) +{ + const kernel_dispatch_sample record{ + 0x1111111111111111UL, 0x2222222222222222UL, 0x3333333333333333UL, + 0x4444444444444444UL, 0x5555555555555555UL, 0x6666666666666666UL, + 0x7777777777777777UL, 0x8888888888888888UL, 0x9999999999999999UL, + 0x11111111U, 0x22222222U, 0x33333333U, + 0x44444444U, 0x55555555U, 0x66666666U, + 0x77777777U, 0x88888888U, 0x9999999AULL + }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, + 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, + 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, + 0x9a, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, scratch_memory_sample) +{ + constexpr std::string_view k_name = "scratch_a"; + const scratch_memory_sample record{ 0x1111111111111111UL, 0x2222222222222222UL, + 0x3333333333333333UL, 0x4444444444444444UL, + 0x5555555555555555UL, k_name, + 0x11111111, 0x22222222, + 0x6666666666666666UL, 0x7777777777777777UL, + 0x8888888888888888UL, 0x9999999AULL }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x63, 0x72, 0x61, + 0x74, 0x63, 0x68, 0x5f, 0x61, 0x00, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, + 0x22, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x9a, + 0x99, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, memory_copy_sample) +{ + constexpr std::string_view k_name = "copy_b"; + const memory_copy_sample record{ 0x1111111111111111UL, 0x2222222222222222UL, + 0x3333333333333333UL, 0x4444444444444444UL, + 0x5555555555555555UL, k_name, + 0x6666666666666666UL, 0x7777777777777777UL, + 0x8888888888888888UL, 0x9999999999999999UL, + 0xAAAAAAAAAAAAAAAAUL, 0xBBBBBBBBULL }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x70, 0x79, + 0x5f, 0x62, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0x00, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, memory_allocate_sample) +{ + constexpr std::string_view k_name = "alloc_c"; + const memory_allocate_sample record{ 0x1111111111111111UL, + 0x2222222222222222UL, + 0x3333333333333333UL, + 0x4444444444444444UL, + k_name, + 0x11111111, + 0x5555555555555555UL, + 0x6666666666666666UL, + 0x7777777777777777UL, + 0x8888888888888888UL, + 0x9999999AULL }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x00, + 0x11, 0x11, 0x11, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x9a, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, region_sample) +{ + constexpr std::string_view k_call_stack = "stack1"; + constexpr std::string_view k_args_str = "arg1"; + constexpr std::string_view k_category = "cat1"; + constexpr std::string_view k_name = "region_d"; + const region_sample record{ 0x1111111111111111UL, + k_name, + 0x2222222222222222UL, + 0x3333333333333333UL, + 0x4444444444444444UL, + 0x5555555555555555UL, + k_call_stack, + k_args_str, + k_category }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x00, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x31, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x61, 0x72, 0x67, 0x31, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x61, 0x74, 0x31, 0x00, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, in_time_sample) +{ + constexpr std::string_view k_call_stack = "stack2"; + constexpr std::string_view k_event_metadata = "meta1"; + constexpr std::string_view k_line_info = "line1"; + constexpr std::string_view k_track_name = "track_e"; + const in_time_sample record{ 0x1111111111111111UL, k_track_name, + 0x2222222222222222UL, k_event_metadata, + 0x3333333333333333UL, 0x4444444444444444UL, + 0x5555555555555555UL, k_call_stack, + k_line_info }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x00, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6d, 0x65, 0x74, 0x61, 0x31, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x00, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, pmc_event_with_sample) +{ + constexpr std::string_view k_call_stack = "stack3"; + constexpr std::string_view k_event_metadata = "meta2"; + constexpr std::string_view k_line_info = "line2"; + constexpr std::string_view k_pmc_info_name = "pmc_1"; + constexpr std::string_view k_track_name = "track_f"; + const pmc_event_with_sample record{ 0x1111111111111111UL, + k_track_name, + 0x2222222222222222UL, + k_event_metadata, + 0x3333333333333333UL, + 0x4444444444444444UL, + 0x5555555555555555UL, + k_call_stack, + k_line_info, + 0x11112222U, + 0x33U, + k_pmc_info_name, + 1.5, + static_cast(0x6666666666666666LL) }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x66, 0x00, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6d, 0x65, 0x74, 0x61, 0x32, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x33, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x00, 0x22, 0x22, 0x11, 0x11, 0x33, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x6d, 0x63, 0x5f, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x01, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, pmc_event_with_sample_empty_optional) +{ + constexpr std::string_view k_call_stack = "stack3"; + constexpr std::string_view k_event_metadata = "meta2"; + constexpr std::string_view k_line_info = "line2"; + constexpr std::string_view k_pmc_info_name = "pmc_1"; + constexpr std::string_view k_track_name = "track_f"; + const pmc_event_with_sample record{ 0x1111111111111111UL, + k_track_name, + 0x2222222222222222UL, + k_event_metadata, + 0x3333333333333333UL, + 0x4444444444444444UL, + 0x5555555555555555UL, + k_call_stack, + k_line_info, + 0x11112222U, + 0x33U, + k_pmc_info_name, + 1.5, + std::nullopt }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x66, 0x00, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6d, 0x65, 0x74, 0x61, 0x32, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x33, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x00, 0x22, 0x22, 0x11, 0x11, 0x33, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x6d, 0x63, 0x5f, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x00, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, backtrace_region_sample) +{ + constexpr std::string_view k_call_stack = "stack4"; + constexpr std::string_view k_category = "cat2"; + constexpr std::string_view k_extdata = "ext1"; + constexpr std::string_view k_line_info = "line3"; + constexpr std::string_view k_name = "btrace_1"; + constexpr std::string_view k_track_name = "track_g"; + const backtrace_region_sample record{ 0x11111111U, 0x2222222222222222UL, + k_track_name, k_name, + 0x3333333333333333UL, 0x4444444444444444UL, + k_category, k_call_stack, + k_line_info, k_extdata }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, + 0x67, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x5f, 0x31, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x61, 0x74, 0x32, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x34, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x78, 0x74, 0x31, 0x00, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, kfd_sample) +{ + constexpr std::string_view k_args_str = "args1"; + constexpr std::string_view k_category = "cat3"; + constexpr std::string_view k_event_metadata = "meta3"; + constexpr std::string_view k_name = "kfd_ev"; + constexpr std::string_view k_pmc_info_name = "pmc_2"; + constexpr std::string_view k_track_name = "track_h"; + const kfd_sample record{ 0x1111111111111111UL, + k_name, + 0x2222222222222222UL, + 0x3333333333333333UL, + k_args_str, + k_category, + k_track_name, + k_event_metadata, + 0x11112222U, + 0x33U, + k_pmc_info_name, + 2.5, + static_cast(0x7777777777777777LL) }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6b, 0x66, 0x64, 0x5f, 0x65, 0x76, 0x00, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, + 0x72, 0x67, 0x73, 0x31, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x63, 0x61, 0x74, 0x33, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x68, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x65, 0x74, 0x61, 0x33, 0x00, + 0x22, 0x22, 0x11, 0x11, 0x33, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x6d, 0x63, 0x5f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x40, 0x01, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, kfd_sample_empty_optional) +{ + constexpr std::string_view k_args_str = "args1"; + constexpr std::string_view k_category = "cat3"; + constexpr std::string_view k_event_metadata = "meta3"; + constexpr std::string_view k_name = "kfd_ev"; + constexpr std::string_view k_pmc_info_name = "pmc_2"; + constexpr std::string_view k_track_name = "track_h"; + const kfd_sample record{ + 0x1111111111111111UL, k_name, 0x2222222222222222UL, 0x3333333333333333UL, + k_args_str, k_category, k_track_name, k_event_metadata, + 0x11112222U, 0x33U, k_pmc_info_name, 2.5, + std::nullopt + }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6b, 0x66, 0x64, 0x5f, 0x65, 0x76, 0x00, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x72, 0x67, 0x73, 0x31, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x61, 0x74, 0x33, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x72, 0x61, 0x63, + 0x6b, 0x5f, 0x68, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, + 0x65, 0x74, 0x61, 0x33, 0x00, 0x22, 0x22, 0x11, 0x11, 0x33, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x6d, 0x63, 0x5f, 0x32, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, cpu_pmc_sample) +{ + const std::vector cores{ + { .cpu_id = 0x1A1A1A1A1A1A1A1AUL, + .frequency = std::bit_cast(0x3C3C3C3CU), + .load = std::bit_cast(0x5D5D5D5D5D5D5D5DUL) }, + { .cpu_id = 0x2B2B2B2B2B2B2B2BUL, + .frequency = std::bit_cast(0x4D4D4D4DU), + .load = std::bit_cast(0x6E6E6E6E6E6E6E6EUL) }, + }; + const auto freqs = rocprofsys::pmc::collectors::cpu::serialize_frequencies(cores); + const auto loads = rocprofsys::pmc::collectors::cpu::serialize_loads(cores); + + auto process_data = rocprofsys::pmc::collectors::cpu::process_metrics{}; + process_data.page_rss = 0x3333333333333333UL; + process_data.virt_mem = 0x4444444444444444UL; + process_data.peak_rss = 0x5555555555555555UL; + process_data.context_switches = 0x66666666UL; + process_data.page_faults = 0x77777777UL; + process_data.user_mode_time = static_cast(0x0888888888888888UL); + process_data.kernel_mode_time = static_cast(0x0999999999999999UL); + + auto enabled = rocprofsys::pmc::collectors::cpu::enabled_metrics{}; + enabled.value = 0x0000A5A5U; + const rocprofsys::pmc::collectors::cpu::sample record{ + enabled, 0x11111111U, 0x2222222222222222UL, process_data, freqs, loads + }; + + constexpr std::array k_expected{ + 0xa5, 0xa5, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x77, 0x77, 0x77, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x08, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x09, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x3c, 0x3c, 0x3c, 0x3c, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x4d, 0x4d, 0x4d, 0x4d, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, ainic_pmc_sample) +{ + auto metrics = rocprofsys::pmc::collectors::nic::metrics{}; + metrics.rx_rdma_ucast_bytes = 0x3333333333333333UL; + metrics.tx_rdma_ucast_bytes = 0x4444444444444444UL; + metrics.rx_rdma_ucast_pkts = 0x5555555555555555UL; + metrics.tx_rdma_ucast_pkts = 0x6666666666666666UL; + metrics.rx_rdma_cnp_pkts = 0x7777777777777777UL; + metrics.tx_rdma_cnp_pkts = 0x8888888888888888UL; + metrics.tx_rdma_ack_timeout = 0x9999999999999999UL; + metrics.resp_tx_pkt_seq_err = 0xAAAAAAAAAAAAAAAAUL; + metrics.req_rx_pkt_seq_err = 0xBBBBBBBBBBBBBBBBUL; + metrics.req_rx_impl_nak_seq_err = 0xCCCCCCCCCCCCCCCCUL; + + auto enabled = rocprofsys::pmc::collectors::nic::enabled_metrics{}; + enabled.value = 0x00005A5AU; + const rocprofsys::pmc::collectors::nic::sample record{ + enabled, 0x11111111U, std::string("nic_dev_a"), 0x2222222222222222UL, metrics + }; + + constexpr std::array k_expected{ + 0x5a, 0x5a, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6e, 0x69, 0x63, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x61, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, hipfile_pmc_sample) +{ + auto metrics = rocprofsys::pmc::collectors::hipfile::metrics{}; + metrics.read_bytes = 0x3333333333333333UL; + metrics.write_bytes = 0x4444444444444444UL; + metrics.read_ops = 0x5555555555555555UL; + metrics.write_ops = 0x6666666666666666UL; + metrics.fastpath_reads = 0x7777777777777777UL; + metrics.fastpath_writes = 0x8888888888888888UL; + metrics.fallback_reads = 0x9999999999999999UL; + metrics.fallback_writes = 0xAAAAAAAAAAAAAAAAUL; + metrics.unaligned_reads = 0xBBBBBBBBBBBBBBBBUL; + metrics.unaligned_writes = 0xCCCCCCCCCCCCCCCCUL; + metrics.read_errors = 0xDDDDDDDDDDDDDDDDUL; + metrics.write_errors = 0xEEEEEEEEEEEEEEEEUL; + metrics.read_bandwidth = 1.5; + metrics.write_bandwidth = 2.5; + metrics.query_failed = false; + + auto enabled = rocprofsys::pmc::collectors::hipfile::enabled_metrics{}; + enabled.value = 0x0000C3C3U; + const rocprofsys::pmc::collectors::hipfile::sample record{ enabled, 0x11111111U, + 0x2222222222222222UL, + metrics }; + + constexpr std::array k_expected{ + 0xc3, 0xc3, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, gpu_pmc_sample) +{ + auto metrics = rocprofsys::backends::amd_smi::gpu::metrics{}; + metrics.average_socket_power = 0x33333333U; + metrics.current_socket_power = 0x44444444U; + metrics.memory_usage = 0x5555555555555555UL; + metrics.hotspot_temperature = static_cast(0x06666666LL); + metrics.edge_temperature = static_cast(0x07777777LL); + metrics.gfx_activity = 0x88888888U; + metrics.umc_activity = 0x99999999U; + metrics.mm_activity = 0xAAAAAAAAU; + metrics.xcp_stats[0].jpeg_busy[0] = 0x1111U; + metrics.xcp_stats[0].jpeg_busy[1] = 0x2222U; + metrics.xcp_stats[0].vcn_busy[0] = 0x3333U; + metrics.xcp_stats[0].vcn_busy[1] = 0x4444U; + metrics.xcp_stats[1].jpeg_busy[0] = 0x5555U; + metrics.xcp_stats[1].jpeg_busy[1] = 0x6666U; + metrics.xcp_stats[1].vcn_busy[0] = 0x7777U; + metrics.xcp_stats[1].vcn_busy[1] = 0x8888U; + metrics.vcn_activity[0] = 0xBBBBU; + metrics.vcn_activity[1] = 0xCCCCU; + metrics.jpeg_activity[0] = 0xDDDDU; + metrics.jpeg_activity[1] = 0xEEEEU; + metrics.xgmi.link.width = 0xFFFFU; + metrics.xgmi.link.speed = 0x1E1EU; + metrics.xgmi.data_acc.read[0] = 0x2222222222222222UL; + metrics.xgmi.data_acc.read[1] = 0x2A2A2A2A2A2A2A2AUL; + metrics.xgmi.data_acc.write[0] = 0x3333333333333333UL; + metrics.xgmi.data_acc.write[1] = 0x3A3A3A3A3A3A3A3AUL; + metrics.pcie.link.width = 0x4E4EU; + metrics.pcie.link.speed = 0x5E5EU; + metrics.pcie.bandwidth.acc = 0x6E6E6E6E6E6E6E6EUL; + metrics.pcie.bandwidth.inst = 0x7E7E7E7E7E7E7E7EUL; + metrics.sdma_usage = 0x8B8B8B8BU; + metrics.gfx_clock_mhz = 0x9C9C9C9CU; + metrics.mem_clock_mhz = 0xADADADADU; + + auto enabled = rocprofsys::pmc::collectors::gpu::enabled_metrics{}; + enabled.value = 0x00001111U; + const rocprofsys::pmc::collectors::gpu::sample record{ enabled, 0x0C0C0C0CU, + 0x1D1D1D1D1D1D1D1DUL, + metrics }; + + constexpr std::array k_expected{ + 0x11, 0x11, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x77, 0x77, 0x77, 0x07, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + 0x99, 0x99, 0x99, 0x99, 0xaa, 0xaa, 0xaa, 0xaa, 0x11, 0x11, 0x22, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x44, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x88, 0x88, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb, 0xbb, 0xcc, + 0xcc, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xdd, 0xee, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1e, 0x1e, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x4e, 0x5e, 0x5e, 0x6e, 0x6e, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, + 0x8b, 0x8b, 0x8b, 0x8b, 0x9c, 0x9c, 0x9c, 0x9c, 0xad, 0xad, 0xad, 0xad, + }; + + expect_golden(record, k_expected); +} + +TEST(record_format, gpu_perf_counter_sample) +{ + auto entries = + std::vector{}; + entries.push_back(rocprofsys::pmc::collectors::gpu_perf_counter::counter_value{ + .counter_id = + static_cast( + 0x11111111U), + .value = 1.5 }); + entries.push_back(rocprofsys::pmc::collectors::gpu_perf_counter::counter_value{ + .counter_id = + static_cast( + 0x33333333U), + .value = 2.5 }); + + const rocprofsys::pmc::collectors::gpu_perf_counter::sample record{ + 0x0C0C0C0CU, 0x5555555555555555UL, std::move(entries) + }; + + constexpr std::array k_expected{ + 0x0c, 0x0c, 0x0c, 0x0c, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x33, 0x33, 0x33, + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, + }; + + expect_golden(record, k_expected); +} +// NOLINTEND(readability-magic-numbers) diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_sample_type.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_sample_type.cpp index 76f88c70fbbc..bbec8b9a775f 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_sample_type.cpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_sample_type.cpp @@ -1,6 +1,7 @@ // Copyright (c) Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT +#include "core/trace_cache/archive.hpp" #include "core/trace_cache/sample_type.hpp" #include "library/pmc/collectors/hipfile/sample.hpp" #include "library/pmc/collectors/hipfile/types.hpp" diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_storage_parser.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_storage_parser.cpp index e3a503f0ca65..bdcc9a274280 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_storage_parser.cpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_storage_parser.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include class sample_processor_t @@ -48,53 +49,39 @@ class sample_processor_t m_expected_samples_5 = samples; } - void execute_sample_processing(test_type_identifier_t type_identifier, - const rocprofsys::trace_cache::cacheable_t& value) + void handle_sample(const test_sample_1& sample) { - switch(type_identifier) - { - case test_type_identifier_t::sample_type_1: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - const size_t idx = m_sample_1_count++; - check_sample_1(idx, sample); - break; - } - case test_type_identifier_t::sample_type_2: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - const size_t idx = m_sample_2_count++; - check_sample_2(idx, sample); - break; - } - case test_type_identifier_t::sample_type_3: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - const size_t idx = m_sample_3_count++; - check_sample_3(idx, sample); - break; - } - case test_type_identifier_t::sample_type_4: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - const size_t idx = m_sample_4_count++; - check_sample_4(idx, sample); - break; - } - case test_type_identifier_t::sample_type_5: - { - const auto& sample = static_cast(value); - const std::lock_guard lock(m_data_mutex); - const size_t idx = m_sample_5_count++; - check_sample_5(idx, sample); - break; - } - default: m_unknown_count++; break; - } + const std::lock_guard lock(m_data_mutex); + const size_t idx = m_sample_1_count++; + check_sample_1(idx, sample); + } + + void handle_sample(const test_sample_2& sample) + { + const std::lock_guard lock(m_data_mutex); + const size_t idx = m_sample_2_count++; + check_sample_2(idx, sample); + } + + void handle_sample(const test_sample_3& sample) + { + const std::lock_guard lock(m_data_mutex); + const size_t idx = m_sample_3_count++; + check_sample_3(idx, sample); + } + + void handle_sample(const test_sample_4& sample) + { + const std::lock_guard lock(m_data_mutex); + const size_t idx = m_sample_4_count++; + check_sample_4(idx, sample); + } + + void handle_sample(const test_sample_5& sample) + { + const std::lock_guard lock(m_data_mutex); + const size_t idx = m_sample_5_count++; + check_sample_5(idx, sample); } int get_sample_1_count() const { return m_sample_1_count.load(); } @@ -102,7 +89,6 @@ class sample_processor_t int get_sample_3_count() const { return m_sample_3_count.load(); } int get_sample_4_count() const { return m_sample_4_count.load(); } int get_sample_5_count() const { return m_sample_5_count.load(); } - int get_unknown_count() const { return m_unknown_count.load(); } private: void check_sample_1(size_t index, const test_sample_1& sample) @@ -150,7 +136,6 @@ class sample_processor_t std::atomic m_sample_3_count{ 0 }; std::atomic m_sample_4_count{ 0 }; std::atomic m_sample_5_count{ 0 }; - std::atomic m_unknown_count{ 0 }; std::vector m_expected_samples_1; std::vector m_expected_samples_2; std::vector m_expected_samples_3; @@ -290,7 +275,7 @@ TEST_F(storage_parser_test, load_multiple_sample_types) EXPECT_EQ(std::remove(test_file_path.c_str()), 0); } -TEST_F(storage_parser_test, load_unsupported_sample_type) +TEST_F(storage_parser_test, load_unsupported_sample_type_throws) { const std::vector samples_1 = { test_sample_1(123, "mixed_test") }; const std::vector samples_2 = { test_sample_2(3.14159, 555), @@ -307,7 +292,7 @@ TEST_F(storage_parser_test, load_unsupported_sample_type) test_sample_2> parser(test_file_path); - EXPECT_NO_THROW(parser.load(processor)); + EXPECT_THROW(parser.load(processor), std::runtime_error); EXPECT_EQ(processor->get_sample_1_count(), 1); EXPECT_EQ(processor->get_sample_2_count(), 2); diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_type_registry.cpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_type_registry.cpp index 7bccade3abcc..4fb08708e598 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_type_registry.cpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/tests/test_type_registry.cpp @@ -1,11 +1,55 @@ // Copyright (c) Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT +#include "core/trace_cache/archive.hpp" #include "core/trace_cache/type_registry.hpp" #include "mocked_types.hpp" #include #include +#include +#include +#include +#include + +static_assert(rocprofsys::trace_cache::detail::are_unique<1, 2, 3>()); +static_assert(!rocprofsys::trace_cache::detail::are_unique<1, 2, 1>()); + +namespace +{ +// Serializes `value`, dispatches it under `type_id` and calls `check` with the record +// the visitor received, while the serialized buffer is still alive (string_view members +// point into it). Returns whether the visitor saw a `Sample`. +template +bool +dispatch_round_trip(const Registry& registry, test_type_identifier_t type_id, + const Sample& value, Check&& check) +{ + std::vector buffer(rocprofsys::trace_cache::get_size(value)); + rocprofsys::trace_cache::serialize(buffer.data(), value); + + auto* data = buffer.data(); + bool received = false; + registry.dispatch(type_id, data, [&](const auto& sample) { + if constexpr(std::is_same_v>) + { + received = true; + check(sample); + } + }); + return received; +} + +template +void +dispatch_counting_calls(const Registry& registry, test_type_identifier_t type_id, + int& calls) +{ + std::uint8_t dummy_data = 0; + std::uint8_t* data = &dummy_data; + registry.dispatch(type_id, data, [&](const auto&) { ++calls; }); +} +} // namespace class type_registry_test : public ::testing::Test { @@ -15,97 +59,64 @@ class type_registry_test : public ::testing::Test type_registry; }; -TEST_F(type_registry_test, test_get_type_sample_1) +TEST_F(type_registry_test, test_dispatch_sample_1) { - const test_sample_1 test_value{ 42, "hello" }; - const size_t buffer_size = rocprofsys::trace_cache::get_size(test_value); - std::vector buffer(buffer_size); - rocprofsys::trace_cache::serialize(buffer.data(), test_value); - - auto* buffer_data = buffer.data(); - auto result = - type_registry.get_type(test_type_identifier_t::sample_type_1, buffer_data); - - ASSERT_TRUE(result.has_value()); - ASSERT_TRUE(std::holds_alternative(result.value())); - - auto const sample_1 = std::get(result.value()); - EXPECT_EQ(sample_1.value, 42); - EXPECT_EQ(sample_1.text, "hello"); + EXPECT_TRUE(dispatch_round_trip(type_registry, test_type_identifier_t::sample_type_1, + test_sample_1{ 42, "hello" }, + [](const test_sample_1& sample) { + EXPECT_EQ(sample.value, 42); + EXPECT_EQ(sample.text, "hello"); + })); } -TEST_F(type_registry_test, test_get_type_sample_2) +TEST_F(type_registry_test, test_dispatch_sample_2) { - const test_sample_2 test_value{ 3.14, 123 }; - const size_t buffer_size = rocprofsys::trace_cache::get_size(test_value); - std::vector buffer(buffer_size); - rocprofsys::trace_cache::serialize(buffer.data(), test_value); - - auto* buffer_data = buffer.data(); - auto result = - type_registry.get_type(test_type_identifier_t::sample_type_2, buffer_data); - - ASSERT_TRUE(result.has_value()); - ASSERT_TRUE(std::holds_alternative(result.value())); - - auto const sample_2 = std::get(result.value()); - EXPECT_DOUBLE_EQ(sample_2.data, 3.14); - EXPECT_EQ(sample_2.sample_id, 123); + EXPECT_TRUE(dispatch_round_trip(type_registry, test_type_identifier_t::sample_type_2, + test_sample_2{ 3.14, 123 }, + [](const test_sample_2& sample) { + EXPECT_DOUBLE_EQ(sample.data, 3.14); + EXPECT_EQ(sample.sample_id, 123); + })); } -TEST_F(type_registry_test, test_get_type_unknown_id) +TEST_F(type_registry_test, test_dispatch_calls_visitor_once) { - std::uint8_t dummy_data = 0; - std::uint8_t* data = &dummy_data; + const test_sample_2 test_value{ 3.14, 123 }; + std::vector buffer(rocprofsys::trace_cache::get_size(test_value)); + rocprofsys::trace_cache::serialize(buffer.data(), test_value); - auto const result = - type_registry.get_type(test_type_identifier_t::fragmented_space, data); + auto* data = buffer.data(); + int calls = 0; + type_registry.dispatch(test_type_identifier_t::sample_type_2, data, + [&](const auto&) { ++calls; }); - EXPECT_FALSE(result.has_value()); + EXPECT_EQ(calls, 1); } -TEST_F(type_registry_test, test_variant_type_definition) +TEST_F(type_registry_test, test_dispatch_unknown_id_throws_without_calling_visitor) { - using expected_variant = std::variant; - using actual_variant = - rocprofsys::trace_cache::type_registry::variant_t; + int calls = 0; - EXPECT_TRUE((std::is_same_v) ); + EXPECT_THROW(dispatch_counting_calls(type_registry, + test_type_identifier_t::fragmented_space, calls), + std::runtime_error); + EXPECT_EQ(calls, 0); } -TEST_F(type_registry_test, test_multiple_calls_same_type) +TEST_F(type_registry_test, test_dispatch_multiple_calls_same_type) { - const test_sample_1 test_value1{ 100, "first" }; - const test_sample_1 test_value2{ 200, "second" }; - - const size_t buffer_size1 = rocprofsys::trace_cache::get_size(test_value1); - const size_t buffer_size2 = rocprofsys::trace_cache::get_size(test_value2); - - std::vector buffer1(buffer_size1); - std::vector buffer2(buffer_size2); - - rocprofsys::trace_cache::serialize(buffer1.data(), test_value1); - rocprofsys::trace_cache::serialize(buffer2.data(), test_value2); - - auto* buffer1_data = buffer1.data(); - auto* buffer2_data = buffer2.data(); - - auto result1 = - type_registry.get_type(test_type_identifier_t::sample_type_1, buffer1_data); - auto result2 = - type_registry.get_type(test_type_identifier_t::sample_type_1, buffer2_data); - - ASSERT_TRUE(result1.has_value()); - ASSERT_TRUE(result2.has_value()); - - auto const sample_1_1 = std::get(result1.value()); - auto const sample_1_2 = std::get(result2.value()); - - EXPECT_EQ(sample_1_1.value, 100); - EXPECT_EQ(sample_1_1.text, "first"); - EXPECT_EQ(sample_1_2.value, 200); - EXPECT_EQ(sample_1_2.text, "second"); + EXPECT_TRUE(dispatch_round_trip(type_registry, test_type_identifier_t::sample_type_1, + test_sample_1{ 100, "first" }, + [](const test_sample_1& sample) { + EXPECT_EQ(sample.value, 100); + EXPECT_EQ(sample.text, "first"); + })); + EXPECT_TRUE(dispatch_round_trip(type_registry, test_type_identifier_t::sample_type_1, + test_sample_1{ 200, "second" }, + [](const test_sample_1& sample) { + EXPECT_EQ(sample.value, 200); + EXPECT_EQ(sample.text, "second"); + })); } class type_registry_optional_test : public ::testing::Test @@ -116,40 +127,20 @@ class type_registry_optional_test : public ::testing::Test type_registry; }; -TEST_F(type_registry_optional_test, test_get_type_sample_5_with_value) +TEST_F(type_registry_optional_test, test_dispatch_sample_5_with_value) { - const test_sample_5 test_value{ std::optional{ 42 } }; - const size_t buffer_size = rocprofsys::trace_cache::get_size(test_value); - std::vector buffer(buffer_size); - rocprofsys::trace_cache::serialize(buffer.data(), test_value); - - auto* buffer_data = buffer.data(); - auto result = - type_registry.get_type(test_type_identifier_t::sample_type_5, buffer_data); - - ASSERT_TRUE(result.has_value()); - ASSERT_TRUE(std::holds_alternative(result.value())); - - auto sample_5 = std::get(result.value()); - ASSERT_TRUE(sample_5.data.has_value()); - EXPECT_EQ(sample_5.data.value(), 42); + EXPECT_TRUE(dispatch_round_trip(type_registry, test_type_identifier_t::sample_type_5, + test_sample_5{ std::optional{ 42 } }, + [](const test_sample_5& sample) { + ASSERT_TRUE(sample.data.has_value()); + EXPECT_EQ(sample.data.value(), 42); + })); } -TEST_F(type_registry_optional_test, test_get_type_sample_5_nullopt) +TEST_F(type_registry_optional_test, test_dispatch_sample_5_nullopt) { - const test_sample_5 test_value{ std::nullopt }; - const size_t buffer_size = rocprofsys::trace_cache::get_size(test_value); - std::vector buffer(buffer_size); - rocprofsys::trace_cache::serialize(buffer.data(), test_value); - - auto* buffer_data = buffer.data(); - auto result = - type_registry.get_type(test_type_identifier_t::sample_type_5, buffer_data); - - ASSERT_TRUE(result.has_value()); - ASSERT_TRUE(std::holds_alternative(result.value())); - - auto const sample_5 = std::get(result.value()); - EXPECT_FALSE(sample_5.data.has_value()); - EXPECT_EQ(sample_5.data, std::nullopt); + EXPECT_TRUE(dispatch_round_trip( + type_registry, test_type_identifier_t::sample_type_5, + test_sample_5{ std::nullopt }, + [](const test_sample_5& sample) { EXPECT_EQ(sample.data, std::nullopt); })); } diff --git a/projects/rocprofiler-systems/source/lib/core/trace_cache/type_registry.hpp b/projects/rocprofiler-systems/source/lib/core/trace_cache/type_registry.hpp index 61a56a09030f..eee04eeeb6fe 100644 --- a/projects/rocprofiler-systems/source/lib/core/trace_cache/type_registry.hpp +++ b/projects/rocprofiler-systems/source/lib/core/trace_cache/type_registry.hpp @@ -2,47 +2,65 @@ // SPDX-License-Identifier: MIT #pragma once +#include "core/trace_cache/archive.hpp" #include "core/trace_cache/cache_type_traits.hpp" #include -#include -#include -#include +#include +#include +#include +#include +#include namespace rocprofsys::trace_cache { +namespace detail +{ +template +[[nodiscard]] constexpr bool +are_unique() +{ + std::array identifiers{ Identifiers... }; + std::ranges::sort(identifiers); + return std::ranges::adjacent_find(identifiers) == identifiers.end(); +} +} // namespace detail + template class type_registry { static_assert(type_traits::is_enum_class_v, "TypeIdentifierEnum must be an enum class"); + static_assert(detail::are_unique(), + "two record types share a type_identifier"); public: - using variant_t = std::variant; - - type_registry() { (register_type(), ...); } - - std::optional get_type(TypeIdentifierEnum id, std::uint8_t*& data) + /** + * Deserializes the record whose type_identifier matches `type_id` and passes it to + * `visitor`. + * @throws std::runtime_error when no supported type has that id. + */ + template + requires type_traits::sample_visitor + void dispatch(TypeIdentifierEnum type_id, std::uint8_t*& data, + Visitor&& visitor) const { - auto const it = deserializers.find(id); - if(it != deserializers.end()) + const auto try_type = [&](std::type_identity) { + if(type_id != T::type_identifier) + { + return false; + } + visitor(deserialize(data)); + return true; + }; + if(!(try_type(std::type_identity{}) || ...)) { - return it->second(data); + throw std::runtime_error( + "No record type registered for type id " + + std::to_string( + static_cast>(type_id))); } - return std::nullopt; - } - -private: - std::map> deserializers; - - template - requires type_traits::cacheable - void register_type() - { - deserializers[T::type_identifier] = [](std::uint8_t*& data) -> variant_t { - return deserialize(data); - }; } }; diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/components/backtrace_metrics.cpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/components/backtrace_metrics.cpp index 07ce24472eba..32322259621e 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/components/backtrace_metrics.cpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/components/backtrace_metrics.cpp @@ -232,7 +232,7 @@ metadata_initialize_backtrace_metrics_pmc(size_t dev_id, const char* _units, trace_cache::get_metadata_registry().add_pmc_info( { agent_type::cpu, dev_id, TARGET_ARCH, EVENT_CODE, INSTANCE_ID, _track_name, trait::name::value, trait::name::description, - LONG_DESCRIPTION, COMPONENT, _units, trace_cache::ABSOLUTE, BLOCK, + LONG_DESCRIPTION, COMPONENT, _units, trace_cache::k_absolute, BLOCK, EXPRESSION, 0, 0, "{}" }); }); } diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/components/comm_data.cpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/components/comm_data.cpp index cfbfb8ce406b..bbe8b229fb9a 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/components/comm_data.cpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/components/comm_data.cpp @@ -7,6 +7,7 @@ #include "core/config.hpp" #include "core/node_info.hpp" #include "core/trace_cache/cache_manager.hpp" +#include "core/trace_cache/cacheable.hpp" #include "core/trace_cache/sample_type.hpp" #include "library/tracing.hpp" #include @@ -77,7 +78,7 @@ metadata_initialize_comm_data_pmc() .long_description = k_long_description, .component = k_component, .units = k_msg, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = k_block, .expression = k_expression, .is_constant = 0, @@ -95,7 +96,7 @@ metadata_initialize_comm_data_pmc() .long_description = k_long_description, .component = k_component, .units = k_msg, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = k_block, .expression = k_expression, .is_constant = 0, @@ -114,7 +115,7 @@ metadata_initialize_comm_data_pmc() .long_description = k_long_description, .component = k_component, .units = k_msg, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = k_block, .expression = k_expression, .is_constant = 0, @@ -132,7 +133,7 @@ metadata_initialize_comm_data_pmc() .long_description = k_long_description, .component = k_component, .units = k_msg, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = k_block, .expression = k_expression, .is_constant = 0, diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/cache_policy.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/cache_policy.hpp index 435cec9e17eb..d6a1aa4b9b1e 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/cache_policy.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/cache_policy.hpp @@ -84,12 +84,12 @@ struct cache_policy .long_description = long_description, .component = component, .units = "MHz", - .value_type = rocprofsys::trace_cache::ABSOLUTE, - .block = block, - .expression = expression, - .is_constant = is_constant, - .is_derived = is_derived, - .extdata = extdata }); + .value_type = rocprofsys::trace_cache::k_absolute, + .block = block, + .expression = expression, + .is_constant = is_constant, + .is_derived = is_derived, + .extdata = extdata }); registry.ensure_track(freq_name, std::nullopt, extdata); const auto load_name = @@ -104,13 +104,13 @@ struct cache_policy .description = "CPU Core Load Percentage", .long_description = long_description, .component = component, - .units = trace_cache::PERCENTAGE, - .value_type = rocprofsys::trace_cache::ABSOLUTE, - .block = block, - .expression = expression, - .is_constant = is_constant, - .is_derived = is_derived, - .extdata = extdata }); + .units = trace_cache::k_percentage, + .value_type = rocprofsys::trace_cache::k_absolute, + .block = block, + .expression = expression, + .is_constant = is_constant, + .is_derived = is_derived, + .extdata = extdata }); registry.ensure_track(load_name, std::nullopt, extdata); } @@ -145,29 +145,29 @@ struct cache_policy add_process_pmc(name::value, "Page RSS", "Process Physical Memory (RSS)", "MB", - rocprofsys::trace_cache::ABSOLUTE); + rocprofsys::trace_cache::k_absolute); add_process_pmc(name::value, "Virt Mem", "Process Virtual Memory", "MB", - rocprofsys::trace_cache::ABSOLUTE); + rocprofsys::trace_cache::k_absolute); add_process_pmc(name::value, "Peak RSS", "Process Peak Memory (HWM)", "MB", - rocprofsys::trace_cache::ABSOLUTE); + rocprofsys::trace_cache::k_absolute); add_process_pmc(name::value, "Ctx Switches", - "Context Switches", "count", rocprofsys::trace_cache::ABSOLUTE); + "Context Switches", "count", rocprofsys::trace_cache::k_absolute); add_process_pmc(name::value, "Page Faults", - "Page Faults", "count", rocprofsys::trace_cache::ABSOLUTE); + "Page Faults", "count", rocprofsys::trace_cache::k_absolute); add_process_pmc(name::value, "User Time", "Process CPU Time in User Mode", "sec", - rocprofsys::trace_cache::ABSOLUTE); + rocprofsys::trace_cache::k_absolute); add_process_pmc(name::value, "Kernel Time", "Process CPU Time in Kernel Mode", "sec", - rocprofsys::trace_cache::ABSOLUTE); + rocprofsys::trace_cache::k_absolute); } /** diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/sample.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/sample.hpp index 70ed0ec58b54..71bb18027aac 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/sample.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/sample.hpp @@ -19,7 +19,7 @@ namespace rocprofsys::pmc::collectors::cpu * Stores enabled metrics, timestamp, process-level data, and serialized * per-CPU frequency/load data as byte vectors. */ -struct sample : trace_cache::cacheable_t +struct sample { static constexpr trace_cache::type_identifier_t type_identifier{ trace_cache::type_identifier_t::cpu_pmc_sample @@ -43,6 +43,12 @@ struct sample : trace_cache::cacheable_t process_metrics process_data{}; std::vector freqs; // serialized cpu_id+freq pairs std::vector loads; // serialized cpu_id+load pairs + + template + void serialize(Archive& archive) + { + archive(enabled_metric, device_id, timestamp, process_data, freqs, loads); + } }; inline std::vector @@ -90,44 +96,6 @@ serialize_loads(const std::vector& cpu_data) namespace rocprofsys::trace_cache { -template <> -inline void -serialize(std::uint8_t* buffer, const pmc::collectors::cpu::sample& item) -{ - utility::store_value(buffer, static_cast(item.enabled_metric.value), - item.device_id, item.timestamp, item.process_data.page_rss, - item.process_data.virt_mem, item.process_data.peak_rss, - item.process_data.context_switches, - item.process_data.page_faults, item.process_data.user_mode_time, - item.process_data.kernel_mode_time, item.freqs, item.loads); -} - -template <> -inline pmc::collectors::cpu::sample -deserialize(std::uint8_t*& buffer) -{ - pmc::collectors::cpu::sample item; - utility::parse_value(buffer, item.enabled_metric.value, item.device_id, - item.timestamp, item.process_data.page_rss, - item.process_data.virt_mem, item.process_data.peak_rss, - item.process_data.context_switches, - item.process_data.page_faults, item.process_data.user_mode_time, - item.process_data.kernel_mode_time, item.freqs, item.loads); - return item; -} - -template <> -inline size_t -get_size(const pmc::collectors::cpu::sample& item) -{ - return utility::get_size( - static_cast(item.enabled_metric.value), item.device_id, - item.timestamp, item.process_data.page_rss, item.process_data.virt_mem, - item.process_data.peak_rss, item.process_data.context_switches, - item.process_data.page_faults, item.process_data.user_mode_time, - item.process_data.kernel_mode_time, item.freqs, item.loads); -} - /// @brief CPU PMC sample type alias using cpu_pmc_sample = pmc::collectors::cpu::sample; diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/tests/test_sample.cpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/tests/test_sample.cpp index f6aa696d35f7..2463046bbcc5 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/tests/test_sample.cpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/tests/test_sample.cpp @@ -1,6 +1,7 @@ // Copyright (c) Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT +#include "core/trace_cache/archive.hpp" #include "library/pmc/collectors/cpu/sample.hpp" #include diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/types.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/types.hpp index 1d50045a3a7c..f0bdc13ea617 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/types.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/cpu/types.hpp @@ -42,6 +42,12 @@ union enabled_metrics std::uint32_t kernel_time : 1; } bits; std::uint32_t value = 0; + + template + void serialize(Archive& archive) + { + archive(value); + } }; /// All 9 CPU metrics enabled (bits 0-8). @@ -71,6 +77,13 @@ struct process_metrics std::int64_t page_faults = 0; // count (major + minor) std::int64_t user_mode_time = 0; // microseconds std::int64_t kernel_mode_time = 0; // microseconds + + template + void serialize(Archive& archive) + { + archive(page_rss, virt_mem, peak_rss, context_switches, page_faults, + user_mode_time, kernel_mode_time); + } }; /** diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/cache_policy.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/cache_policy.hpp index 8a521af9bb1e..25ac4a6f24ce 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/cache_policy.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/cache_policy.hpp @@ -5,6 +5,7 @@ #include "core/config.hpp" #include "core/trace_cache/cache_manager.hpp" +#include "core/trace_cache/cacheable.hpp" #include "core/trace_cache/metadata_registry.hpp" #include "library/pmc/collectors/gpu/sample.hpp" #include "library/pmc/collectors/gpu/types.hpp" @@ -175,8 +176,8 @@ struct cache_policy .description = trait::name::description, .long_description = LONG_DESCRIPTION, .component = COMPONENT, - .units = trace_cache::PERCENTAGE, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .units = trace_cache::k_percentage, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -194,8 +195,8 @@ struct cache_policy .description = trait::name::description, .long_description = LONG_DESCRIPTION, .component = COMPONENT, - .units = trace_cache::PERCENTAGE, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .units = trace_cache::k_percentage, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -213,8 +214,8 @@ struct cache_policy .description = trait::name::description, .long_description = LONG_DESCRIPTION, .component = COMPONENT, - .units = trace_cache::PERCENTAGE, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .units = trace_cache::k_percentage, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -233,7 +234,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = CELSIUS_DEGREES, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -252,7 +253,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "W", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -271,7 +272,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "MB", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -294,8 +295,8 @@ struct cache_policy .description = "VCN (Video Decode) Engine Activity", .long_description = LONG_DESCRIPTION, .component = COMPONENT, - .units = trace_cache::PERCENTAGE, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .units = trace_cache::k_percentage, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -320,8 +321,8 @@ struct cache_policy .description = "JPEG (Image Decode) Engine Activity", .long_description = LONG_DESCRIPTION, .component = COMPONENT, - .units = trace_cache::PERCENTAGE, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .units = trace_cache::k_percentage, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -348,8 +349,8 @@ struct cache_policy .description = "VCN (Video Decode) Engine Activity", .long_description = LONG_DESCRIPTION, .component = COMPONENT, - .units = trace_cache::PERCENTAGE, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .units = trace_cache::k_percentage, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -376,8 +377,8 @@ struct cache_policy .description = "JPEG (Image Decode) Engine Activity", .long_description = LONG_DESCRIPTION, .component = COMPONENT, - .units = trace_cache::PERCENTAGE, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .units = trace_cache::k_percentage, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -397,8 +398,8 @@ struct cache_policy .description = trait::name::description, .long_description = LONG_DESCRIPTION, .component = COMPONENT, - .units = trace_cache::PERCENTAGE, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .units = trace_cache::k_percentage, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -417,7 +418,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "MHz", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -436,7 +437,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "MHz", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -455,7 +456,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "lanes", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -474,7 +475,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "Mbps", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -499,7 +500,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "KB", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -521,7 +522,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "KB", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -541,7 +542,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "lanes", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -560,7 +561,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "MT/s", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -580,7 +581,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "bytes", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, @@ -600,7 +601,7 @@ struct cache_policy .long_description = LONG_DESCRIPTION, .component = COMPONENT, .units = "bytes/s", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = BLOCK, .expression = EXPRESSION, .is_constant = 0, diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/sample.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/sample.hpp index 0750d87403ed..9746c3f50a51 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/sample.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/sample.hpp @@ -3,10 +3,13 @@ #pragma once +#include "backends/amd_smi/gpu_types.hpp" +#include "core/trace_cache/cache_type_traits.hpp" #include "core/trace_cache/sample_type.hpp" #include "library/pmc/collectors/gpu/types.hpp" #include +#include namespace rocprofsys::pmc::collectors::gpu { @@ -16,7 +19,7 @@ namespace rocprofsys::pmc::collectors::gpu * * This struct represents a sample of GPU performance metrics collected by the PMC. */ -struct sample : trace_cache::cacheable_t +struct sample { static constexpr trace_cache::type_identifier_t type_identifier{ trace_cache::type_identifier_t::gpu_pmc_sample @@ -35,72 +38,45 @@ struct sample : trace_cache::cacheable_t std::uint32_t device_id = 0; std::uint64_t timestamp = 0; metrics metric_values{}; + + template + void serialize(Archive& archive) + { + // The byte layout of the cache: gpu::metrics is a backend type and does not + // serialize itself, so its fields are listed here in byte order. + archive(enabled_metric, device_id, timestamp, metric_values.average_socket_power, + metric_values.current_socket_power, metric_values.memory_usage, + metric_values.hotspot_temperature, metric_values.edge_temperature, + metric_values.gfx_activity, metric_values.umc_activity, + metric_values.mm_activity, metric_values.xcp_stats, + metric_values.vcn_activity, metric_values.jpeg_activity, + metric_values.xgmi.link.width, metric_values.xgmi.link.speed, + metric_values.xgmi.data_acc.read, metric_values.xgmi.data_acc.write, + metric_values.pcie.link.width, metric_values.pcie.link.speed, + metric_values.pcie.bandwidth.acc, metric_values.pcie.bandwidth.inst, + metric_values.sdma_usage, metric_values.gfx_clock_mhz, + metric_values.mem_clock_mhz); + } }; } // namespace rocprofsys::pmc::collectors::gpu -namespace rocprofsys::trace_cache +namespace rocprofsys::trace_cache::type_traits { -template <> -inline void -serialize(std::uint8_t* buffer, const pmc::collectors::gpu::sample& item) -{ - utility::store_value( - buffer, static_cast(item.enabled_metric.value), item.device_id, - item.timestamp, item.metric_values.average_socket_power, - item.metric_values.current_socket_power, item.metric_values.memory_usage, - item.metric_values.hotspot_temperature, item.metric_values.edge_temperature, - item.metric_values.gfx_activity, item.metric_values.umc_activity, - item.metric_values.mm_activity, item.metric_values.xcp_stats, - item.metric_values.vcn_activity, item.metric_values.jpeg_activity, - item.metric_values.xgmi.link.width, item.metric_values.xgmi.link.speed, - item.metric_values.xgmi.data_acc.read, item.metric_values.xgmi.data_acc.write, - item.metric_values.pcie.link.width, item.metric_values.pcie.link.speed, - item.metric_values.pcie.bandwidth.acc, item.metric_values.pcie.bandwidth.inst, - item.metric_values.sdma_usage, item.metric_values.gfx_clock_mhz, - item.metric_values.mem_clock_mhz); -} +// xcp_stats is cached as raw bytes: it is plain arrays of std::uint16_t, without padding. +static_assert(std::has_unique_object_representations_v< + backends::amd_smi::gpu::metrics::xcp_metrics>, + "xcp_metrics is cached as raw bytes and must not have padding"); template <> -inline pmc::collectors::gpu::sample -deserialize(std::uint8_t*& buffer) -{ - pmc::collectors::gpu::sample item; - utility::parse_value( - buffer, item.enabled_metric.value, item.device_id, item.timestamp, - item.metric_values.average_socket_power, item.metric_values.current_socket_power, - item.metric_values.memory_usage, item.metric_values.hotspot_temperature, - item.metric_values.edge_temperature, item.metric_values.gfx_activity, - item.metric_values.umc_activity, item.metric_values.mm_activity, - item.metric_values.xcp_stats, item.metric_values.vcn_activity, - item.metric_values.jpeg_activity, item.metric_values.xgmi.link.width, - item.metric_values.xgmi.link.speed, item.metric_values.xgmi.data_acc.read, - item.metric_values.xgmi.data_acc.write, item.metric_values.pcie.link.width, - item.metric_values.pcie.link.speed, item.metric_values.pcie.bandwidth.acc, - item.metric_values.pcie.bandwidth.inst, item.metric_values.sdma_usage, - item.metric_values.gfx_clock_mhz, item.metric_values.mem_clock_mhz); - return item; -} +struct explicitly_raw : std::true_type +{}; -template <> -inline size_t -get_size(const pmc::collectors::gpu::sample& item) +} // namespace rocprofsys::trace_cache::type_traits + +namespace rocprofsys::trace_cache { - return utility::get_size( - item.enabled_metric.value, item.device_id, item.timestamp, - item.metric_values.average_socket_power, item.metric_values.current_socket_power, - item.metric_values.memory_usage, item.metric_values.hotspot_temperature, - item.metric_values.edge_temperature, item.metric_values.gfx_activity, - item.metric_values.umc_activity, item.metric_values.mm_activity, - item.metric_values.xcp_stats, item.metric_values.vcn_activity, - item.metric_values.jpeg_activity, item.metric_values.xgmi.link.width, - item.metric_values.xgmi.link.speed, item.metric_values.xgmi.data_acc.read, - item.metric_values.xgmi.data_acc.write, item.metric_values.pcie.link.width, - item.metric_values.pcie.link.speed, item.metric_values.pcie.bandwidth.acc, - item.metric_values.pcie.bandwidth.inst, item.metric_values.sdma_usage, - item.metric_values.gfx_clock_mhz, item.metric_values.mem_clock_mhz); -} /// @brief GPU PMC sample type alias using gpu_pmc_sample = pmc::collectors::gpu::sample; diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/types.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/types.hpp index 083daaee212e..f9b39c5ed023 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/types.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu/types.hpp @@ -73,6 +73,12 @@ union enabled_metrics std::uint32_t mem_clock : 1; // current_uclk (MHz) } bits; std::uint32_t value = 0; + + template + void serialize(Archive& archive) + { + archive(value); + } }; // Socket power: prefer the instantaneous "current" reading, falling back to the diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/cache_policy.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/cache_policy.hpp index 6738d97d562b..af2a2563bf51 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/cache_policy.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/cache_policy.hpp @@ -66,12 +66,12 @@ struct cache_policy .long_description = long_description, .component = component, .units = "count", - .value_type = rocprofsys::trace_cache::ABSOLUTE, - .block = meta.block, - .expression = meta.expression, - .is_constant = meta.is_constant ? 1U : 0U, - .is_derived = meta.is_derived ? 1U : 0U, - .extdata = "{}" }); + .value_type = rocprofsys::trace_cache::k_absolute, + .block = meta.block, + .expression = meta.expression, + .is_constant = meta.is_constant ? 1U : 0U, + .is_derived = meta.is_derived ? 1U : 0U, + .extdata = "{}" }); name_entries.push_back({ .counter_id = meta.counter_id, .pmc_info_name = std::move(qname), diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/sample.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/sample.hpp index 25900aff85b4..3a36668341fe 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/sample.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/sample.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -22,7 +23,7 @@ namespace rocprofsys::pmc::collectors::gpu_perf_counter * rocprofiler_sample_device_counting_service(). Each entry carries its * own qualified counter name including dimension positions. */ -struct sample : trace_cache::cacheable_t +struct sample { static constexpr trace_cache::type_identifier_t type_identifier{ trace_cache::type_identifier_t::gpu_perf_counter_sample @@ -38,58 +39,30 @@ struct sample : trace_cache::cacheable_t std::uint32_t device_id = 0; std::uint64_t timestamp = 0; std::vector entries; + + template + void serialize(Archive& archive) + { + archive(device_id, timestamp, entries); + } }; } // namespace rocprofsys::pmc::collectors::gpu_perf_counter -namespace rocprofsys::trace_cache +namespace rocprofsys::trace_cache::type_traits { -/// @brief SDK PMC sample type alias -using gpu_perf_counter_sample = pmc::collectors::gpu_perf_counter::sample; - +// counter_value has no padding (see its static_assert), so its bytes are the format. template <> -inline void -serialize(std::uint8_t* buffer, const pmc::collectors::gpu_perf_counter::sample& item) -{ - size_t pos = 0; - const auto num_entries = static_cast(item.entries.size()); - utility::store_value(item.device_id, buffer, pos); - utility::store_value(item.timestamp, buffer, pos); - utility::store_value(num_entries, buffer, pos); - for(const auto& entry : item.entries) - { - utility::store_value(entry.counter_id, buffer, pos); - utility::store_value(entry.value, buffer, pos); - } -} +struct explicitly_raw : std::true_type +{}; -template <> -inline pmc::collectors::gpu_perf_counter::sample -deserialize(std::uint8_t*& buffer) -{ - pmc::collectors::gpu_perf_counter::sample item; - std::uint32_t num_entries = 0; - utility::parse_value(buffer, item.device_id, item.timestamp, num_entries); - item.entries.resize(num_entries); - for(auto& entry : item.entries) - { - utility::parse_value(buffer, entry.counter_id, entry.value); - } - return item; -} +} // namespace rocprofsys::trace_cache::type_traits -template <> -inline size_t -get_size(const pmc::collectors::gpu_perf_counter::sample& item) +namespace rocprofsys::trace_cache { - size_t total_size = utility::get_size( - item.device_id, item.timestamp, static_cast(item.entries.size())); - for(const auto& entry : item.entries) - { - total_size += utility::get_size(entry.counter_id, entry.value); - } - return total_size; -} + +/// @brief SDK PMC sample type alias +using gpu_perf_counter_sample = pmc::collectors::gpu_perf_counter::sample; } // namespace rocprofsys::trace_cache diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/tests/test_sample.cpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/tests/test_sample.cpp index 53c3addcb528..2966d02efb4e 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/tests/test_sample.cpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/tests/test_sample.cpp @@ -1,11 +1,14 @@ // Copyright (c) Advanced Micro Devices, Inc. // SPDX-License-Identifier: MIT +#include "core/trace_cache/archive.hpp" #include "library/pmc/collectors/gpu_perf_counter/sample.hpp" +#include "library/pmc/collectors/gpu_perf_counter/types.hpp" #include #include +#include #include using namespace rocprofsys::pmc::collectors::gpu_perf_counter; @@ -85,9 +88,9 @@ TEST_F(SdkPmcSampleTest, GetSizeEmpty) { const sample test_sample{ 0, 0, {} }; - // device_id(4) + timestamp(8) + num_entries(4) = 16 + // device_id(4) + timestamp(8) + entries byte count(8) = 20 const size_t expected = - sizeof(std::uint32_t) + sizeof(std::uint64_t) + sizeof(std::uint32_t); + sizeof(std::uint32_t) + sizeof(std::uint64_t) + sizeof(std::size_t); EXPECT_EQ(get_size(test_sample), expected); } @@ -95,9 +98,9 @@ TEST_F(SdkPmcSampleTest, GetSizeSingleEntry) { const sample test_sample{ 0, 0, { counter_value{ .counter_id = 10, .value = 1.0 } } }; - // header: 4 + 8 + 4 = 16 + // header: 4 + 8 + 8 = 20 // entry: counter_id(8) + double(8) = 16 - const size_t expected = 16 + sizeof(counter_id_t) + sizeof(double); + const size_t expected = 20 + sizeof(counter_id_t) + sizeof(double); EXPECT_EQ(get_size(test_sample), expected); } @@ -111,11 +114,12 @@ TEST_F(SdkPmcSampleTest, GetSizeMatchesSerializedBytes) const size_t computed_size = get_size(original); serialize(buffer.data(), original); - std::uint8_t* ptr = buffer.data(); - deserialize(ptr); - const size_t bytes_consumed = static_cast(ptr - buffer.data()); + std::uint8_t* ptr = buffer.data(); + const sample deserialized = deserialize(ptr); + const size_t bytes_consumed = static_cast(ptr - buffer.data()); EXPECT_EQ(bytes_consumed, computed_size); + EXPECT_EQ(deserialized.entries.size(), original.entries.size()); } TEST_F(SdkPmcSampleTest, DeserializePreservesBufferPointerAdvancement) diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/types.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/types.hpp index 2f5b7a0ff87f..08bdf18de5ee 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/types.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/gpu_perf_counter/types.hpp @@ -33,6 +33,9 @@ struct counter_value double value{ 0.0 }; }; +static_assert(sizeof(counter_value) == sizeof(counter_id_t) + sizeof(double), + "counter_value is cached as raw bytes and must not have padding"); + using metrics = std::vector; struct counter_definition diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/cache_policy.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/cache_policy.hpp index 6dbc6598415f..119e3d1cb3a2 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/cache_policy.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/cache_policy.hpp @@ -67,7 +67,7 @@ struct cache_policy { agent_type::gpu, gpu_id, TARGET_ARCH, EVENT_CODE, INSTANCE_ID, pmc_name(metric.suffix), metric.suffix, trait::name::description, LONG_DESCRIPTION, - COMPONENT, metric.unit, rocprofsys::trace_cache::ABSOLUTE, BLOCK, + COMPONENT, metric.unit, rocprofsys::trace_cache::k_absolute, BLOCK, EXPRESSION, 0, 0, "{}" }); } } diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/sample.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/sample.hpp index 2000a5abe8e3..f91da2ca6133 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/sample.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/sample.hpp @@ -23,7 +23,7 @@ namespace rocprofsys::pmc::collectors::hipfile * the sampling thread that all the other collectors share. Here the names are resolved * once during post-processing, from METRIC_TABLE, so the sampling path stores a POD. */ -struct sample : trace_cache::cacheable_t +struct sample { static constexpr trace_cache::type_identifier_t type_identifier{ trace_cache::type_identifier_t::hipfile_pmc_sample @@ -42,6 +42,12 @@ struct sample : trace_cache::cacheable_t std::uint32_t device_id = 0; std::uint64_t timestamp = 0; metrics metric_values{}; + + template + void serialize(Archive& archive) + { + archive(enabled_metric, device_id, timestamp, metric_values); + } }; } // namespace rocprofsys::pmc::collectors::hipfile @@ -49,58 +55,6 @@ struct sample : trace_cache::cacheable_t namespace rocprofsys::trace_cache { -// metrics::query_failed is deliberately not carried: cache_policy::store_sample drops a -// failed query before it reaches the buffer, so every stored sample has it false and the -// default-constructed value round-trips correctly. -// Field order after timestamp is metrics member order, excluding query_failed (dropped -// before store). serialize, deserialize, and get_size must list the same fields. -template <> -inline void -serialize(std::uint8_t* buffer, const pmc::collectors::hipfile::sample& item) -{ - utility::store_value( - buffer, static_cast(item.enabled_metric.value), item.device_id, - item.timestamp, item.metric_values.read_bytes, item.metric_values.write_bytes, - item.metric_values.read_ops, item.metric_values.write_ops, - item.metric_values.fastpath_reads, item.metric_values.fastpath_writes, - item.metric_values.fallback_reads, item.metric_values.fallback_writes, - item.metric_values.unaligned_reads, item.metric_values.unaligned_writes, - item.metric_values.read_errors, item.metric_values.write_errors, - item.metric_values.read_bandwidth, item.metric_values.write_bandwidth); -} - -template <> -inline pmc::collectors::hipfile::sample -deserialize(std::uint8_t*& buffer) -{ - pmc::collectors::hipfile::sample item; - utility::parse_value( - buffer, item.enabled_metric.value, item.device_id, item.timestamp, - item.metric_values.read_bytes, item.metric_values.write_bytes, - item.metric_values.read_ops, item.metric_values.write_ops, - item.metric_values.fastpath_reads, item.metric_values.fastpath_writes, - item.metric_values.fallback_reads, item.metric_values.fallback_writes, - item.metric_values.unaligned_reads, item.metric_values.unaligned_writes, - item.metric_values.read_errors, item.metric_values.write_errors, - item.metric_values.read_bandwidth, item.metric_values.write_bandwidth); - return item; -} - -template <> -inline size_t -get_size(const pmc::collectors::hipfile::sample& item) -{ - return utility::get_size( - item.enabled_metric.value, item.device_id, item.timestamp, - item.metric_values.read_bytes, item.metric_values.write_bytes, - item.metric_values.read_ops, item.metric_values.write_ops, - item.metric_values.fastpath_reads, item.metric_values.fastpath_writes, - item.metric_values.fallback_reads, item.metric_values.fallback_writes, - item.metric_values.unaligned_reads, item.metric_values.unaligned_writes, - item.metric_values.read_errors, item.metric_values.write_errors, - item.metric_values.read_bandwidth, item.metric_values.write_bandwidth); -} - /// @brief hipFile PMC sample type alias using hipfile_pmc_sample = pmc::collectors::hipfile::sample; diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/types.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/types.hpp index 5dd6ce5f1c75..6110b3e210bc 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/types.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/hipfile/types.hpp @@ -53,6 +53,12 @@ union enabled_metrics std::uint32_t write_bandwidth : 1; } bits; std::uint32_t value = 0; + + template + void serialize(Archive& archive) + { + archive(value); + } }; /** @@ -93,6 +99,18 @@ struct metrics /// Default false, which is what a value-initialized `metrics{}` needs: the paused /// collector emits exactly that to drop the counter tracks to zero. bool query_failed = false; + + template + void serialize(Archive& archive) + { + // query_failed is deliberately not carried: cache_policy::store_sample drops + // a failed query before it reaches the buffer, so every stored sample has it + // false and the default-constructed value round-trips correctly. + archive(read_bytes, write_bytes, read_ops, write_ops, fastpath_reads, + fastpath_writes, fallback_reads, fallback_writes, unaligned_reads, + unaligned_writes, read_errors, write_errors, read_bandwidth, + write_bandwidth); + } }; /** diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/cache_policy.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/cache_policy.hpp index 822d65a174a7..d2885ffa6b02 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/cache_policy.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/cache_policy.hpp @@ -95,7 +95,7 @@ struct cache_policy .long_description = k_long_description, .component = k_component, .units = units, - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = k_block, .expression = k_expression, .is_constant = 0, diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/sample.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/sample.hpp index 81e5dfd2465b..2ff2c9ee95d3 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/sample.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/sample.hpp @@ -8,7 +8,6 @@ #include #include -#include namespace rocprofsys::pmc::collectors::nic { @@ -17,9 +16,8 @@ namespace rocprofsys::pmc::collectors::nic * @brief NIC RDMA sample type for trace cache. * * This struct represents a sample of NIC RDMA metrics collected by the PMC. - * It inherits from cacheable_t to support serialization to the trace cache. */ -struct sample : trace_cache::cacheable_t +struct sample { static constexpr trace_cache::type_identifier_t type_identifier{ trace_cache::type_identifier_t::ainic_pmc_sample @@ -40,6 +38,12 @@ struct sample : trace_cache::cacheable_t std::string device_name; std::uint64_t timestamp; metrics metric_values; + + template + void serialize(Archive& archive) + { + archive(enabled_metric, device_id, device_name, timestamp, metric_values); + } }; } // namespace rocprofsys::pmc::collectors::nic @@ -50,51 +54,4 @@ namespace rocprofsys::trace_cache /// @brief AINIC PMC sample type alias using ainic_pmc_sample = pmc::collectors::nic::sample; -template <> -inline void -serialize(std::uint8_t* buffer, const pmc::collectors::nic::sample& item) -{ - utility::store_value( - buffer, static_cast(item.enabled_metric.value), item.device_id, - std::string_view(item.device_name), item.timestamp, - item.metric_values.rx_rdma_ucast_bytes, item.metric_values.tx_rdma_ucast_bytes, - item.metric_values.rx_rdma_ucast_pkts, item.metric_values.tx_rdma_ucast_pkts, - item.metric_values.rx_rdma_cnp_pkts, item.metric_values.tx_rdma_cnp_pkts, - item.metric_values.tx_rdma_ack_timeout, item.metric_values.resp_tx_pkt_seq_err, - item.metric_values.req_rx_pkt_seq_err, - item.metric_values.req_rx_impl_nak_seq_err); -} - -template <> -inline pmc::collectors::nic::sample -deserialize(std::uint8_t*& buffer) -{ - pmc::collectors::nic::sample item; - std::string_view device_name_view; - utility::parse_value( - buffer, item.enabled_metric.value, item.device_id, device_name_view, - item.timestamp, item.metric_values.rx_rdma_ucast_bytes, - item.metric_values.tx_rdma_ucast_bytes, item.metric_values.rx_rdma_ucast_pkts, - item.metric_values.tx_rdma_ucast_pkts, item.metric_values.rx_rdma_cnp_pkts, - item.metric_values.tx_rdma_cnp_pkts, item.metric_values.tx_rdma_ack_timeout, - item.metric_values.resp_tx_pkt_seq_err, item.metric_values.req_rx_pkt_seq_err, - item.metric_values.req_rx_impl_nak_seq_err); - item.device_name = std::string(device_name_view); - return item; -} - -template <> -inline size_t -get_size(const pmc::collectors::nic::sample& item) -{ - return utility::get_size( - item.enabled_metric.value, item.device_id, std::string_view(item.device_name), - item.timestamp, item.metric_values.rx_rdma_ucast_bytes, - item.metric_values.tx_rdma_ucast_bytes, item.metric_values.rx_rdma_ucast_pkts, - item.metric_values.tx_rdma_ucast_pkts, item.metric_values.rx_rdma_cnp_pkts, - item.metric_values.tx_rdma_cnp_pkts, item.metric_values.tx_rdma_ack_timeout, - item.metric_values.resp_tx_pkt_seq_err, item.metric_values.req_rx_pkt_seq_err, - item.metric_values.req_rx_impl_nak_seq_err); -} - } // namespace rocprofsys::trace_cache diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/types.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/types.hpp index f6b19856c552..0a67140a4c82 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/types.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/pmc/collectors/nic/types.hpp @@ -51,6 +51,12 @@ union enabled_metrics std::uint32_t req_rx_impl_nak_seq_err : 1; } bits; std::uint32_t value = 0; + + template + void serialize(Archive& archive) + { + archive(value); + } }; // Bit-mask for all NIC RDMA metrics enabled @@ -74,6 +80,15 @@ struct metrics std::uint64_t resp_tx_pkt_seq_err = 0; std::uint64_t req_rx_pkt_seq_err = 0; std::uint64_t req_rx_impl_nak_seq_err = 0; + + template + void serialize(Archive& archive) + { + archive(rx_rdma_ucast_bytes, tx_rdma_ucast_bytes, rx_rdma_ucast_pkts, + tx_rdma_ucast_pkts, rx_rdma_cnp_pkts, tx_rdma_cnp_pkts, + tx_rdma_ack_timeout, resp_tx_pkt_seq_err, req_rx_pkt_seq_err, + req_rx_impl_nak_seq_err); + } }; } // namespace rocprofsys::pmc::collectors::nic diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk.cpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk.cpp index 32a9d5ff6fcb..b3fc6251afa0 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk.cpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk.cpp @@ -452,7 +452,7 @@ struct external_dependencies // Single source of truth is core/trace_cache/cacheable.hpp's ABSOLUTE constant; // kfd_events.hpp never includes that header, so the value is surfaced here. - static constexpr std::string_view k_pmc_value_type_absolute = trace_cache::ABSOLUTE; + static constexpr std::string_view k_pmc_value_type_absolute = trace_cache::k_absolute; // Single source of truth for these strings is core/categories.hpp's // trait::name; kfd_events.hpp itself never includes diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.cpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.cpp index 04a96eefd009..97183ae54d3a 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.cpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.cpp @@ -5,6 +5,7 @@ #include "core/agent_manager.hpp" #include "core/demangler.hpp" #include "core/trace_cache/cache_manager.hpp" +#include "core/trace_cache/cacheable.hpp" #include "core/trace_cache/metadata_registry.hpp" #include "library/rocprofiler-sdk/fwd.hpp" #include @@ -56,7 +57,7 @@ metadata_initialize_counters_pmc(size_t dev_id, const std::string& name, .long_description = long_description, .component = component, .units = "Unit Count", - .value_type = rocprofsys::trace_cache::ABSOLUTE, + .value_type = rocprofsys::trace_cache::k_absolute, .block = block, .expression = expression, .is_constant = 0,