Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0562cb1
test(rocprofiler-systems): pin the trace cache record byte format
adjordje-amd Oct 22, 2026
a7295e1
feat(rocprofiler-systems): add trace cache archives
adjordje-amd Oct 22, 2026
dd50fec
refactor(rocprofiler-systems): serialize trace cache records through …
adjordje-amd Oct 22, 2026
cd576bf
refactor(rocprofiler-systems): let nested PMC metric types serialize …
adjordje-amd Oct 22, 2026
475a150
perf(rocprofiler-systems): keep trace cache archive state in registers
adjordje-amd Oct 22, 2026
c7a43bf
perf(rocprofiler-systems): iterate gpu_perf_counter entries with a ra…
adjordje-amd Oct 22, 2026
a7b7600
perf(rocprofiler-systems): default-initialize the record read from th…
adjordje-amd Oct 22, 2026
0ca50c7
refactor(rocprofiler-systems): use plain inline and [[gnu::flatten]] …
adjordje-amd Oct 22, 2026
86c09ca
perf(rocprofiler-systems): build records in place in the type registry
adjordje-amd Oct 22, 2026
652ad2e
style(rocprofiler-systems): trim comments in the trace cache archives
adjordje-amd Oct 22, 2026
13e8fe6
feat(rocprofiler-systems): check trace cache type identifiers at comp…
adjordje-amd Oct 22, 2026
990fc13
refactor(rocprofiler-systems): remove unused tuple_to_variant trait
adjordje-amd Oct 22, 2026
bf9c4c5
refactor(rocprofiler-systems): drop the variadic utility helpers and …
adjordje-amd Oct 23, 2026
76911d3
test(rocprofiler-systems): pin the per-core CPU entry bytes in the go…
adjordje-amd Oct 23, 2026
ccc1bde
refactor(rocprofiler-systems): name the archive per-value helpers aft…
adjordje-amd Oct 23, 2026
0751825
refactor(rocprofiler-systems): serialize the record header like the r…
adjordje-amd Oct 23, 2026
6118506
perf(rocprofiler-systems): read cached vectors with one memcpy
adjordje-amd Oct 23, 2026
d920330
refactor(rocprofiler-systems): cache gpu_perf_counter entries as one …
adjordje-amd Oct 23, 2026
14ff24e
refactor(rocprofiler-systems): cache std::string and enums directly, …
adjordje-amd Oct 23, 2026
94f4284
fix(rocprofiler-systems): read only whole elements of a cached vector
adjordje-amd Oct 24, 2026
049ee3e
refactor(rocprofiler-systems): list the gpu metrics layout in the gpu…
adjordje-amd Oct 24, 2026
3d1f0aa
fix(rocprofiler-systems): reject record fields the archives would cop…
adjordje-amd Oct 24, 2026
4cfd7b2
refactor(rocprofiler-systems): dispatch cached records by type id, dr…
adjordje-amd Oct 26, 2026
727e916
fix(rocprofiler-systems): use the renamed trace cache constants at ev…
adjordje-amd Oct 28, 2026
e5ae422
Merge remote-tracking branch 'origin/develop' into users/adjordje-amd…
adjordje-amd Nov 3, 2026
35dcc65
Merge remote-tracking branch 'origin/users/adjordje-amd/trace-cache-a…
adjordje-amd Nov 3, 2026
48fc38f
fix(rocprofiler-systems): resolve clang-tidy and fixedwidth-types CI …
adjordje-amd Nov 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
164 changes: 164 additions & 0 deletions projects/rocprofiler-systems/source/lib/core/trace_cache/archive.hpp
Original file line number Diff line number Diff line change
@@ -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 <cstddef>
#include <cstdint>
#include <cstring>
#include <type_traits>

namespace rocprofsys::trace_cache
{

static_assert(std::is_same_v<std::size_t, std::uint64_t>,
"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 <typename Type, typename Archive>
concept archive_serializable =
std::is_same_v<decltype(&Type::template serialize<Archive>),
void (Type::*)(Archive&)>;

struct binary_size_archive
{
std::size_t size = 0;

template <typename... Types>
constexpr void operator()(const Types&... values)
{
(measure_value(values), ...);
}

private:
template <typename Type>
constexpr void measure_value(const Type& value)
{
if constexpr(archive_serializable<Type, binary_size_archive>)
{
const_cast<Type&>(value).serialize(*this);
}
else
{
size += utility::get_size(value);
}
}
};

struct binary_write_archive
{
std::uint8_t* buffer = nullptr;
std::size_t position = 0;

template <typename... Types>
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 <typename Type>
void write_value(const Type& value, std::uint8_t* out, std::size_t& pos)
{
if constexpr(archive_serializable<Type, binary_write_archive>)
{
position = pos;
const_cast<Type&>(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 <typename... Types>
void operator()(Types&... values)
{
std::uint8_t* cur = cursor;
(read_value(values, cur), ...);
cursor = cur;
}

private:
template <typename Type>
void read_value(Type& value, std::uint8_t*& cur)
{
if constexpr(archive_serializable<Type, binary_read_archive>)
{
cursor = cur;
value.serialize(*this);
cur = cursor;
}
else
{
utility::parse_value(cur, value);
}
}
};

template <typename T>
concept archive_record =
std::is_default_constructible_v<T> && requires { T::type_identifier; } &&
archive_serializable<T, binary_size_archive> &&
archive_serializable<T, binary_write_archive> &&
archive_serializable<T, binary_read_archive>;

// 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 <archive_record T>
[[nodiscard, gnu::flatten]] std::size_t
get_size(const T& record)
{
binary_size_archive archive;
const_cast<T&>(record).serialize(archive);
return archive.size;
}

template <archive_record T>
[[gnu::flatten]] void
serialize(std::uint8_t* buffer, const T& record)
{
binary_write_archive archive{ .buffer = buffer, .position = 0 };
const_cast<T&>(record).serialize(archive);
}

template <archive_record T>
[[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 <typename T, typename TypeIdentifierEnum>
concept cacheable = archive_record<T> && has_type_identifier<T, TypeIdentifierEnum>;
} // namespace type_traits

} // namespace rocprofsys::trace_cache
Original file line number Diff line number Diff line change
Expand Up @@ -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(); });
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -137,11 +139,8 @@ class buffer_storage
"Trying to use buffered storage while it is not running");
}

using TypeIdentifierEnumUderlayingType =
std::underlying_type_t<TypeIdentifierEnum>;

size_t const sample_size = get_size(value);
size_t const bytes_to_reserve = header_size<TypeIdentifierEnum> + sample_size;
const record_header<TypeIdentifierEnum> 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
Expand All @@ -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<TypeIdentifierEnumUderlayingType>(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<TypeIdentifierEnum> + header.size);
write_record(buf, header, value);
}

[[nodiscard]] __attribute__((always_inline)) bool is_running() const
Expand All @@ -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;
}
Expand All @@ -202,7 +195,7 @@ class buffer_storage
else
{
ofs.write(reinterpret_cast<const char*>(m_buffer->data() + _tail),
buffer_size - _tail);
k_buffer_size - _tail);
ofs.write(reinterpret_cast<const char*>(m_buffer->data()), _head);
}
if(ofs.fail())
Expand All @@ -217,14 +210,14 @@ class buffer_storage
{
auto* _data = m_buffer->data();
memset(_data + m_head, std::numeric_limits<std::uint8_t>::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<TypeIdentifierEnum>;
std::memcpy(_data + m_head + sizeof(TypeIdentifierEnum), &remaining_bytes,
sizeof(size_t));
const record_header<TypeIdentifierEnum> header{
.type = TypeIdentifierEnum::fragmented_space,
.size = k_buffer_size - m_head - k_header_size<TypeIdentifierEnum>
};
binary_write_archive archive{ .buffer = _data + m_head, .position = 0 };
archive(header);

m_head = 0;
}
Expand All @@ -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<TypeIdentifierEnum>) >
buffer_size,
if(__builtin_expect((m_head + number_of_bytes +
k_header_size<TypeIdentifierEnum>) > k_buffer_size,
0))
{
fragment_memory();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,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(trace_cache::k_tmp_directory);
LOG_TRACE("Found {} files in temp directory", temp_directory_content.size());

const auto cache_files =
Expand Down
Loading
Loading