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
598 lines
18 KiB
C++
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);
|
|
}
|
|
}
|