Files
TheRock/rocm-systems/projects/rocprof-trace-decoder/test/unit/trace_parser_test.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

598 lines
18 KiB
C++

// MIT License
//
// Copyright (c) 2024-2026 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "trace_parser.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <array>
#include <cstring>
#include <vector>
#include "gfx10/gfx10token.h"
#include "gfx9/gfx9token.h"
#include "rocprof_trace_decoder/rocprof_trace_decoder.h"
#include "stitch/stitch.hpp"
// Forward declarations for internal functions
std::unique_ptr<SQTTParser> AnalyseBinary_GFX9_internal(
CppReturnInfo& info,
const uint8_t* tokendata,
uint64_t buffersize,
int target_cu,
class Stitcher& stitch,
bool double_buffer,
bool is_mi350
);
// Note: ToPcV2 is defined in segment_test.cpp
// Tests for CppReturnInfo
TEST(CppReturnInfoTest, DefaultConstruction)
{
CppReturnInfo info;
EXPECT_TRUE(info.perfevents.empty());
EXPECT_EQ(info.realtime_frequency, 0);
EXPECT_EQ(info.counter_frequency, 0);
EXPECT_FALSE(info.bPacketLost);
EXPECT_EQ(info.globaltime, 0);
EXPECT_EQ(info.basetime, 0);
}
// Tests for occupancy_info_t
// Note: 'start' is a 1-bit field: 1 = wave_start, 0 = wave_end
TEST(OccupancyInfoTest, ConstructorWithUint64WaveStart)
{
pcinfo_t pc{100, 5};
occupancy_info_t occ(
pc, 1000, uint64_t(1), uint64_t(2), uint64_t(3), uint64_t(1), uint64_t(0), uint64_t(0), uint64_t(0), uint64_t(0)
);
EXPECT_EQ(occ.pc.address, 100);
EXPECT_EQ(occ.pc.code_object_id, 5);
EXPECT_EQ(occ.time, 1000);
EXPECT_EQ(occ.cu, 1);
EXPECT_EQ(occ.simd, 2);
EXPECT_EQ(occ.wave_id, 3);
EXPECT_EQ(occ.start, 1); // wave_start
}
TEST(OccupancyInfoTest, ConstructorWithUint64WaveEnd)
{
pcinfo_t pc{100, 5};
occupancy_info_t occ(
pc, 1000, uint64_t(1), uint64_t(2), uint64_t(3), uint64_t(0), uint64_t(0), uint64_t(0), uint64_t(0), uint64_t(0)
);
EXPECT_EQ(occ.start, 0); // wave_end
}
TEST(OccupancyInfoTest, ConstructorWithInt8)
{
pcinfo_t pc{200, 10};
occupancy_info_t occ(
pc, 2000, int8_t(4), int8_t(1), int8_t(7), uint64_t(1), uint64_t(0), uint64_t(0), uint64_t(0), uint64_t(0)
);
EXPECT_EQ(occ.pc.address, 200);
EXPECT_EQ(occ.pc.code_object_id, 10);
EXPECT_EQ(occ.time, 2000);
EXPECT_EQ(occ.cu, 4);
EXPECT_EQ(occ.simd, 1);
EXPECT_EQ(occ.wave_id, 7);
EXPECT_EQ(occ.start, 1); // wave_start
}
// Note: bValid tests are in stitch_utils_test.cpp
// Tests for PipeArray64
TEST(PipeArray64Test, SetLoSetsLowBits)
{
PipeArray64 arr{};
struct MockToken
{
int me;
int pipe;
};
MockToken t{0, 0};
arr.setlo(t, 0xDEADBEEF);
EXPECT_EQ(arr.at_reg(t) & 0xFFFFFFFF, 0xDEADBEEF);
}
TEST(PipeArray64Test, SetHiSetsHighBits)
{
PipeArray64 arr{};
struct MockToken
{
int me;
int pipe;
};
MockToken t{0, 0};
arr.sethi(t, 0xCAFEBABE);
EXPECT_EQ(arr.at_reg(t) >> 32, 0xCAFEBABE);
}
TEST(PipeArray64Test, SetLoPreservesHighBits)
{
PipeArray64 arr{};
struct MockToken
{
int me;
int pipe;
};
MockToken t{0, 0};
arr.sethi(t, 0xAAAAAAAA);
arr.setlo(t, 0xBBBBBBBB);
EXPECT_EQ(arr.at_reg(t), 0xAAAAAAAABBBBBBBBull);
}
TEST(PipeArray64Test, SetHiPreservesLowBits)
{
PipeArray64 arr{};
struct MockToken
{
int me;
int pipe;
};
MockToken t{0, 0};
arr.setlo(t, 0xCCCCCCCC);
arr.sethi(t, 0xDDDDDDDD);
EXPECT_EQ(arr.at_reg(t), 0xDDDDDDDDCCCCCCCCull);
}
// Tests for TokenGenerator base class behavior
class TestTokenGenerator : public TokenGenerator
{
public:
TestTokenGenerator(const uint8_t* buf, size_t size, int64_t gt, int64_t bt) : TokenGenerator(buf, size, gt, bt) {}
};
TEST(TokenGeneratorTest, ConstructorSetsFields)
{
uint8_t buffer[100];
TestTokenGenerator gen(buffer, 100, 5000, 1000);
EXPECT_EQ(gen.BUFFER_SIZE, 100);
EXPECT_EQ(gen.get_time(), 5000);
EXPECT_EQ(gen.get_base_time(), 1000);
}
// Edge case: minimum buffer size
TEST(TokenGeneratorEdgeCaseTest, MinimumBufferSize)
{
uint8_t buffer[16]; // Minimum practical size
TestTokenGenerator gen(buffer, 16, 0, 0);
EXPECT_EQ(gen.BUFFER_SIZE, 16);
}
// Edge case: large time values
TEST(TokenGeneratorEdgeCaseTest, LargeTimeValues)
{
uint8_t buffer[100];
int64_t large_time = 0x7FFFFFFFFFFFF; // Large but not max
TestTokenGenerator gen(buffer, 100, large_time, large_time - 1000);
EXPECT_EQ(gen.get_time(), large_time);
EXPECT_EQ(gen.get_base_time(), large_time - 1000);
}
// Edge case: PipeArray64 combines hi and lo correctly
TEST(PipeArray64EdgeCaseTest, HiLoComposition)
{
PipeArray64 arr{};
struct MockToken
{
int me;
int pipe;
};
MockToken t{0, 0};
arr.sethi(t, 0x12345678);
arr.setlo(t, 0xABCDEF00);
EXPECT_EQ(arr.at_reg(t), 0x12345678ABCDEF00ull);
}
// Tests for CSRegisterHandler
struct FakeRegToken
{
int me = 0;
int pipe = 0;
uint32_t regaddr = 0;
uint64_t regdata = 0;
};
TEST(CSRegisterHandlerTest, AddressClassification)
{
CSRegisterHandler h;
EXPECT_TRUE(h.IsUserdata(0xC340));
EXPECT_TRUE(h.IsUserdata(0xC343));
EXPECT_FALSE(h.IsUserdata(0xC344));
EXPECT_TRUE(h.IsUserdata0(0xC340));
EXPECT_TRUE(h.IsUserdata1(0xC341));
EXPECT_TRUE(h.IsUserdata2(0xC342));
EXPECT_TRUE(h.IsUserdata3(0xC343));
}
TEST(CSRegisterHandlerTest, UpdateRegNoCSNonUserdata2Ignored)
{
CSRegisterHandler h;
FakeRegToken tok{0, 0, 0xC340, 0};
EXPECT_EQ(h.UpdateRegNoCS(tok).kind, CSRegisterHandler::RegUpdateEvent::NONE);
}
TEST(CSRegisterHandlerTest, ROCHeaderAndCodeObjLoadUnload)
{
CSRegisterHandler h;
FakeRegToken tok{0, 0, 0xC342, 0};
// Activate ROC format
rocprof_trace_decoder_instrument_enable_t en{};
en.char1 = '\0';
en.char2 = 'R';
en.char3 = 'O';
en.char4 = 'C';
tok.regdata = en.u32All;
h.UpdateRegNoCS(tok);
ASSERT_TRUE(h.bIsROCMFormat);
// CODEOBJ ADDR_LO header + payload
rocprof_trace_decoder_packet_header_t pkt{};
pkt.opcode = ROCPROF_TRACE_DECODER_PACKET_OPCODE_CODEOBJ;
pkt.type = ROCPROF_TRACE_DECODER_CODEOBJ_MARKER_TYPE_ADDR_LO;
tok.regdata = pkt.u32All;
h.UpdateRegNoCS(tok);
tok.regdata = 0x1000;
h.UpdateRegNoCS(tok);
// ADDR_HI
pkt.type = ROCPROF_TRACE_DECODER_CODEOBJ_MARKER_TYPE_ADDR_HI;
tok.regdata = pkt.u32All;
h.UpdateRegNoCS(tok);
tok.regdata = 0;
h.UpdateRegNoCS(tok);
// SIZE_LO
pkt.type = ROCPROF_TRACE_DECODER_CODEOBJ_MARKER_TYPE_SIZE_LO;
tok.regdata = pkt.u32All;
h.UpdateRegNoCS(tok);
tok.regdata = 0x100;
h.UpdateRegNoCS(tok);
// TAIL (load)
pkt.type = ROCPROF_TRACE_DECODER_CODEOBJ_MARKER_TYPE_TAIL;
tok.regdata = pkt.u32All;
h.UpdateRegNoCS(tok);
rocprof_trace_decoder_codeobj_marker_tail_t tail{};
tail.isUnload = 0;
tail.bFromStart = 1;
tail.legacy_id = 42;
tok.regdata = tail.raw;
h.UpdateRegNoCS(tok);
EXPECT_FALSE(h.active_codeobjs.read().empty());
// TAIL (unload)
tok.regdata = pkt.u32All;
h.UpdateRegNoCS(tok);
tail.isUnload = 1;
tok.regdata = tail.raw;
h.UpdateRegNoCS(tok);
EXPECT_TRUE(h.active_codeobjs.read().empty());
}
TEST(CSRegisterHandlerTest, AgentInfoCounterAndRtFrequency)
{
CSRegisterHandler h;
h.bIsROCMFormat = true;
h.userdata_state = ROCPROF_TRACE_DECODER_CODEOBJ_MARKER_TYPE_LAST;
FakeRegToken tok{0, 0, 0xC342, 0};
rocprof_trace_decoder_packet_header_t pkt{};
pkt.opcode = ROCPROF_TRACE_DECODER_PACKET_OPCODE_AGENT_INFO;
// Counter interval
pkt.type = ROCPROF_TRACE_DECODER_AGENT_INFO_TYPE_COUNTER_INTERVAL;
pkt.data20 = 256;
tok.regdata = pkt.u32All;
EXPECT_EQ(h.UpdateRegNoCS(tok).kind, CSRegisterHandler::RegUpdateEvent::COUNTER_FREQUENCY_CHANGED);
EXPECT_EQ(h.counter_frequency, 256u);
// RT frequency
pkt.type = ROCPROF_TRACE_DECODER_AGENT_INFO_TYPE_RT_FREQUENCY_KHZ;
pkt.data20 = 100;
tok.regdata = pkt.u32All;
EXPECT_EQ(h.UpdateRegNoCS(tok).kind, CSRegisterHandler::RegUpdateEvent::NONE);
EXPECT_EQ(h.realtime_frequency, 100u * 1000);
}
// Tests for AnalyseBinary dispatch
namespace
{
rocprofiler_thread_trace_decoder_status_t
noop_cb(rocprofiler_thread_trace_decoder_record_type_t, void*, uint64_t, void*)
{
return ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS;
}
class AnalyseBinaryMock : public ICodeServicer
{
public:
MOCK_METHOD(assemblyLine, GetInstruction, (pcinfo_t addr, int gfxip), (override));
};
struct Gfx9ParserSink
{
std::vector<rocprofiler_thread_trace_decoder_event_t> events{};
std::vector<rocprofiler_thread_trace_decoder_occupancy_t> occupancy{};
};
rocprofiler_thread_trace_decoder_status_t collect_gfx9_records(
rocprofiler_thread_trace_decoder_record_type_t type, void* data, uint64_t count, void* userdata
)
{
auto& sink = *static_cast<Gfx9ParserSink*>(userdata);
if (type == ROCPROFILER_THREAD_TRACE_DECODER_RECORD_EVENT)
{
auto* records = static_cast<rocprofiler_thread_trace_decoder_event_t*>(data);
sink.events.insert(sink.events.end(), records, records + count);
}
else if (type == ROCPROFILER_THREAD_TRACE_DECODER_RECORD_OCCUPANCY)
{
auto* records = static_cast<rocprofiler_thread_trace_decoder_occupancy_t*>(data);
sink.occupancy.insert(sink.occupancy.end(), records, records + count);
}
return ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS;
}
void append_gfx9_word(std::vector<uint8_t>& data, uint64_t word, size_t bytes)
{
for (size_t i = 0; i < bytes; ++i) data.push_back(static_cast<uint8_t>((word >> (8 * i)) & 0xFF));
}
uint64_t encode_gfx9_reg(uint8_t me, uint8_t pipe, uint16_t regaddr, uint32_t regdata)
{
const uint64_t enc_me = (1u - (me & 1u)) & 1u;
return static_cast<uint64_t>(gfx9::TOKEN_REG) | (static_cast<uint64_t>(pipe & 0x3) << 5) | (enc_me << 7) |
(uint64_t{gfx9::Reg::REG_TYPE_USERDATA} << 10) | (uint64_t{1} << 15) |
(static_cast<uint64_t>(regaddr) << 16) | (static_cast<uint64_t>(regdata) << 32);
}
void append_gfx9_userdata2(std::vector<uint8_t>& data, uint32_t regdata)
{
append_gfx9_word(data, encode_gfx9_reg(0, 0, CSRegisterHandler::USERDATA_ADDR_2, regdata), 8);
}
void append_gfx9_codeobj_field(
std::vector<uint8_t>& data, rocprof_trace_decoder_codeobj_marker_type_t type, uint32_t payload
)
{
rocprof_trace_decoder_packet_header_t header{};
header.opcode = ROCPROF_TRACE_DECODER_PACKET_OPCODE_CODEOBJ;
header.type = type;
append_gfx9_userdata2(data, header.u32All);
append_gfx9_userdata2(data, payload);
}
void append_gfx9_codeobj_load_unload(std::vector<uint8_t>& data, uint32_t code_object_id)
{
rocprof_trace_decoder_instrument_enable_t enable{};
enable.char1 = '\0';
enable.char2 = 'R';
enable.char3 = 'O';
enable.char4 = 'C';
append_gfx9_userdata2(data, enable.u32All);
append_gfx9_codeobj_field(data, ROCPROF_TRACE_DECODER_CODEOBJ_MARKER_TYPE_ADDR_LO, 0x1000);
append_gfx9_codeobj_field(data, ROCPROF_TRACE_DECODER_CODEOBJ_MARKER_TYPE_SIZE_LO, 0x200);
rocprof_trace_decoder_codeobj_marker_tail_t tail{};
tail.bFromStart = 1;
tail.legacy_id = code_object_id;
append_gfx9_codeobj_field(data, ROCPROF_TRACE_DECODER_CODEOBJ_MARKER_TYPE_TAIL, tail.raw);
tail.isUnload = 1;
append_gfx9_codeobj_field(data, ROCPROF_TRACE_DECODER_CODEOBJ_MARKER_TYPE_TAIL, tail.raw);
}
void append_gfx9_counter_interval(std::vector<uint8_t>& data, uint32_t interval)
{
rocprof_trace_decoder_packet_header_t header{};
header.opcode = ROCPROF_TRACE_DECODER_PACKET_OPCODE_AGENT_INFO;
header.type = ROCPROF_TRACE_DECODER_AGENT_INFO_TYPE_COUNTER_INTERVAL;
header.data20 = interval;
append_gfx9_userdata2(data, header.u32All);
}
void append_gfx9_perf(
std::vector<uint8_t>& data, uint8_t cu, uint8_t bank, std::array<uint16_t, 4> counters, bool advance_time = true
)
{
uint64_t word = static_cast<uint64_t>(gfx9::TOKEN_PERF);
if (advance_time) word |= uint64_t{1} << 4;
word |= static_cast<uint64_t>(cu & 0xF) << 6;
word |= static_cast<uint64_t>(bank & 0x3) << 10;
word |= static_cast<uint64_t>(counters[0] & 0x1FFF) << 12;
word |= static_cast<uint64_t>(counters[1] & 0x1FFF) << 25;
word |= static_cast<uint64_t>(counters[2] & 0x1FFF) << 38;
word |= static_cast<uint64_t>(counters[3] & 0x1FFF) << 51;
append_gfx9_word(data, word, 8);
}
void append_gfx9_misc_time_delta(std::vector<uint8_t>& data, uint8_t delta)
{
const uint16_t word = static_cast<uint16_t>(gfx9::TOKEN_MISC) | (static_cast<uint16_t>(delta) << 4);
append_gfx9_word(data, word, 2);
}
void append_gfx9_wave_end(std::vector<uint8_t>& data, uint8_t cu, uint8_t simd, uint8_t wave)
{
uint16_t word = static_cast<uint16_t>(gfx9::TOKEN_WAVE_END);
word |= static_cast<uint16_t>(cu & 0xF) << 6;
word |= static_cast<uint16_t>(wave & 0xF) << 10;
word |= static_cast<uint16_t>(simd & 0x3) << 14;
append_gfx9_word(data, word, 2);
}
} // namespace
TEST(AnalyseBinaryTest, InvalidBufferReturnsNull)
{
auto mock = std::make_shared<AnalyseBinaryMock>();
Stitcher stitch(mock, noop_cb, nullptr);
uint8_t buf[8] = {0};
CppReturnInfo info;
EXPECT_EQ(AnalyseBinary_internal(info, buf, sizeof(buf), -1, stitch), nullptr);
}
TEST(AnalyseBinaryTest, Gfx9PositiveTargetCu)
{
auto mock = std::make_shared<AnalyseBinaryMock>();
Stitcher stitch(mock, noop_cb, nullptr);
uint8_t buf[64] = {0};
CppReturnInfo info;
EXPECT_NE(AnalyseBinary_GFX9_internal(info, buf, sizeof(buf), 0, stitch, false, false), nullptr);
}
TEST(AnalyseBinaryGfx9Test, EmitsCodeObjectPerfAndRecoveredOccupancyRecords)
{
constexpr uint32_t code_object_id = 42;
std::vector<uint8_t> buffer;
append_gfx9_codeobj_load_unload(buffer, code_object_id);
append_gfx9_wave_end(buffer, 1, 2, 3);
append_gfx9_counter_interval(buffer, 4);
append_gfx9_perf(buffer, 0, 1, {11, 22, 33, 44});
append_gfx9_misc_time_delta(buffer, 5);
append_gfx9_perf(buffer, 0, 1, {55, 66, 77, 88});
auto mock = std::make_shared<AnalyseBinaryMock>();
Gfx9ParserSink sink;
Stitcher stitch(mock, collect_gfx9_records, &sink);
CppReturnInfo info;
EXPECT_NE(AnalyseBinary_GFX9_internal(info, buffer.data(), buffer.size(), 0, stitch, false, false), nullptr);
ASSERT_EQ(sink.occupancy.size(), 2u);
EXPECT_EQ(sink.occupancy[0].start, 1u);
EXPECT_EQ(sink.occupancy[1].start, 0u);
for (const auto& occ : sink.occupancy)
{
EXPECT_EQ(occ.time, 0u);
EXPECT_EQ(occ.cu, 1u);
EXPECT_EQ(occ.simd, 2u);
EXPECT_EQ(occ.wave_id, 3u);
}
ASSERT_EQ(sink.events.size(), 2u);
EXPECT_EQ(sink.events[0].type, ROCPROF_TRACE_DECODER_EVENT_CODE_OBJECT_LOAD);
EXPECT_EQ(sink.events[0].payload.raw, code_object_id);
EXPECT_EQ(sink.events[1].type, ROCPROF_TRACE_DECODER_EVENT_CODE_OBJECT_UNLOAD);
EXPECT_EQ(sink.events[1].payload.raw, code_object_id);
EXPECT_EQ(info.counter_frequency, 4u);
ASSERT_EQ(info.perfevents.size(), 4u);
EXPECT_EQ(info.perfevents[0].time, 4);
EXPECT_EQ(info.perfevents[0].bank, 1u);
EXPECT_EQ(info.perfevents[0].events0, 11u);
EXPECT_EQ(info.perfevents[0].events3, 44u);
EXPECT_EQ(info.perfevents[1].time, 28);
EXPECT_EQ(info.perfevents[1].events0, 0u);
EXPECT_EQ(info.perfevents[2].time, 28);
EXPECT_EQ(info.perfevents[2].events0, 55u);
EXPECT_EQ(info.perfevents[2].events3, 88u);
EXPECT_EQ(info.perfevents[3].time, 32);
EXPECT_EQ(info.perfevents[3].events0, 0u);
}
namespace
{
std::array<uint8_t, sizeof(uint64_t)> bytes_from_word(uint64_t word)
{
std::array<uint8_t, sizeof(uint64_t)> bytes{};
std::memcpy(bytes.data(), &word, sizeof(word));
return bytes;
}
} // namespace
TEST(DetectArchInternalTest, SniffsSupportedHeadersAndRejectsInvalidBuffers)
{
EXPECT_EQ(DetectArch_internal(nullptr, sizeof(uint64_t)), TraceArch::UNKNOWN);
std::array<uint8_t, sizeof(uint64_t)> zeros{};
EXPECT_EQ(DetectArch_internal(zeros.data(), sizeof(uint64_t) - 1), TraceArch::UNKNOWN);
EXPECT_EQ(DetectArch_internal(zeros.data(), zeros.size()), TraceArch::UNKNOWN);
rocprof_trace_decoder_gfx9_header_t gfx9{};
gfx9.legacy_version = 0;
gfx9.gfx9_version2 = 5;
auto bytes = bytes_from_word(gfx9.raw);
EXPECT_EQ(DetectArch_internal(bytes.data(), bytes.size()), TraceArch::GFX9);
gfx9.legacy_version = 0x11;
gfx9.gfx9_version2 = 6;
bytes = bytes_from_word(gfx9.raw);
EXPECT_EQ(DetectArch_internal(bytes.data(), bytes.size()), TraceArch::GFX9);
gfx9.legacy_version = 0;
gfx9.gfx9_version2 = 7;
bytes = bytes_from_word(gfx9.raw);
EXPECT_EQ(DetectArch_internal(bytes.data(), bytes.size()), TraceArch::UNKNOWN);
struct rdna_case_t
{
uint8_t version;
TraceArch expected;
};
const std::array<rdna_case_t, 6> cases{
rdna_case_t{1, TraceArch::GFX10 },
rdna_case_t{2, TraceArch::GFX10 },
rdna_case_t{3, TraceArch::GFX11 },
rdna_case_t{4, TraceArch::GFX12 },
rdna_case_t{5, TraceArch::MI400 },
rdna_case_t{6, TraceArch::UNKNOWN},
};
for (const auto& c : cases)
{
header_type header{};
header.header = 0b0010001;
header.version = c.version;
bytes = bytes_from_word(header.raw);
EXPECT_EQ(DetectArch_internal(bytes.data(), bytes.size()), c.expected) << "version=" << int(c.version);
}
}