diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/CMakeLists.txt b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/CMakeLists.txt index 26e44d9558..5dc9f77271 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/CMakeLists.txt +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/CMakeLists.txt @@ -1,8 +1,5 @@ -set(ROCPROFILER_LIB_PC_SAMPLING_PARSER_SOURCES pc_record_interface.cpp correlation.cpp - translation.cpp) -set(ROCPROFILER_LIB_PC_SAMPLING_PARSER_HEADERS - correlation.hpp gfx9.hpp gfx11.hpp gfx_unknown.hpp parser_types.hpp pc_record_interface.hpp - rocr.hpp translation.hpp) +set(ROCPROFILER_LIB_PC_SAMPLING_PARSER_SOURCES pc_record_interface.cpp) +file(GLOB ROCPROFILER_LIB_PC_SAMPLING_PARSER_HEADERS *.h *.hpp) target_sources( rocprofiler-object-library PRIVATE ${ROCPROFILER_LIB_PC_SAMPLING_PARSER_SOURCES} diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/correlation.cpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/correlation.cpp index 1978bc3788..45db722a55 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/correlation.cpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/correlation.cpp @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "lib/rocprofiler-sdk/pc_sampling/parser/correlation.hpp" +#include "correlation.hpp" template <> struct std::hash diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/correlation.hpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/correlation.hpp index 80a6aae96f..3270b63c68 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/correlation.hpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/correlation.hpp @@ -28,20 +28,19 @@ #include #include -#include "lib/rocprofiler-sdk/pc_sampling/parser/translation.hpp" +#include "translation.hpp" -#if 0 template <> struct std::hash { size_t operator()(const device_handle& d) const { return d.handle; } }; -bool +bool inline operator==(device_handle a, device_handle b) { return a.handle == b.handle; } -#endif + namespace Parser { /* @@ -56,13 +55,12 @@ struct DispatchPkt uint64_t correlation_id_in; //! Correlation ID seen by the trap handler device_handle dev; //! Which device this is run }; -#if 0 -bool + +inline bool operator==(const DispatchPkt& a, const DispatchPkt& b) { return a.correlation_id_in == b.correlation_id_in && a.dev == b.dev; } -#endif } // namespace Parser template <> @@ -89,22 +87,50 @@ public: * Checks wether a dispatch pkt will generate a collision. * Returns true on collision and false when slot is available. */ - bool checkDispatch(const dispatch_pkt_id_t& pkt) const; + bool checkDispatch(const dispatch_pkt_id_t& pkt) const + { + uint64_t trap = wrap_correlation_id(pkt.doorbell_id, pkt.write_index, pkt.queue_size); + return dispatch_to_correlation.find({trap, pkt.device}) != dispatch_to_correlation.end(); + } /** * Updates the mapping of dispatch_id to correlation_id */ - void newDispatch(const dispatch_pkt_id_t& pkt); + void newDispatch(const dispatch_pkt_id_t& pkt) + { + cache_dev_id = ~0ul; + uint64_t trap_id = wrap_correlation_id(pkt.doorbell_id, pkt.write_index, pkt.queue_size); + dispatch_to_correlation[{trap_id, pkt.device}] = pkt.correlation_id; + } - void forget(const dispatch_pkt_id_t& pkt); + void forget(const dispatch_pkt_id_t& pkt) + { + cache_dev_id = ~0ul; + uint64_t trap_id = wrap_correlation_id(pkt.doorbell_id, pkt.write_index, pkt.queue_size); + dispatch_to_correlation.erase({trap_id, pkt.device}); + } /** * Given a device dev, doorbell and and wrapped dispatch_id, returns the * correlation_id set by dispatch_pkt_id_t */ - uint64_t get(device_handle dev, uint64_t correlation_in); + uint64_t get(device_handle dev, uint64_t correlation_in) + { +#ifndef _PARSER_CORRELATION_DISABLE_CACHE + if(dev.handle == cache_dev_id && correlation_in == cache_correlation_id_in) + return cache_correlation_id_out; +#endif + cache_dev_id = dev.handle; + cache_correlation_id_in = correlation_in; + cache_correlation_id_out = dispatch_to_correlation.at({correlation_in, dev}); + return cache_correlation_id_out; + } - static uint64_t wrap_correlation_id(uint64_t doorbell, uint64_t write_idx, uint64_t queue_size); + static uint64_t wrap_correlation_id(uint64_t doorbell, uint64_t write_idx, uint64_t queue_size) + { + static constexpr uint64_t WRITE_WRAP = (1 << 25) - 1; + return ((write_idx % queue_size) & WRITE_WRAP) | (uint64_t(doorbell) << 32); + } private: std::unordered_map dispatch_to_correlation{}; @@ -141,7 +167,7 @@ add_upcoming_samples(const device_handle device, } template -pcsample_status_t +inline pcsample_status_t _parse_buffer(generic_sample_t* buffer, uint64_t buffer_size, user_callback_t callback, @@ -222,9 +248,23 @@ _parse_buffer(generic_sample_t* buffer, * a size smaller than requested, then it may be called again requesting more memory. * @param[in] userdata parameter forwarded to the user callback. */ -pcsample_status_t -parse_buffer(generic_sample_t* buffer, - uint64_t buffer_size, - int gfxip_major, - user_callback_t callback, - void* userdata); +pcsample_status_t inline parse_buffer(generic_sample_t* buffer, + uint64_t buffer_size, + int gfxip_major, + user_callback_t callback, + void* userdata) +{ + static auto corr_map = std::make_unique(); + + auto parseSample_func = _parse_buffer; + if(gfxip_major == 9) + parseSample_func = _parse_buffer; + else if(gfxip_major == 11) + parseSample_func = _parse_buffer; + else if(gfxip_major == 0) + parseSample_func = _parse_buffer; + else + return PCSAMPLE_STATUS_INVALID_GFXIP; + + return parseSample_func(buffer, buffer_size, callback, userdata, corr_map.get()); +}; diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/parser_types.hpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/parser_types.h similarity index 91% rename from projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/parser_types.hpp rename to projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/parser_types.h index fc420308bf..5542c9567a 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/parser_types.hpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/parser_types.h @@ -22,12 +22,6 @@ #pragma once -#include -#include -#include -#include -#include - /** * ######## Parser Definitions ######## */ @@ -84,13 +78,17 @@ enum pcsample_arb_issue_state }; }; // namespace PCSAMPLE -typedef struct +typedef union { - uint8_t valid : 1; - uint8_t type : 4; // 0=reserved, 1=hosttrap, 2=stochastic, 3=perfcounter, >=4 possible v2? - uint8_t has_stall_reason : 1; - uint8_t has_wave_cnt : 1; - uint8_t has_memory_counter : 1; + struct + { + uint8_t valid : 1; + uint8_t type : 4; // 0=reserved, 1=hosttrap, 2=stochastic, 3=perfcounter, >=4 possible v2? + uint8_t has_stall_reason : 1; + uint8_t has_wave_cnt : 1; + uint8_t has_memory_counter : 1; + }; + uint8_t raw; } pcsample_header_v1_t; typedef struct @@ -128,9 +126,9 @@ typedef struct uint64_t pc; uint64_t exec_mask; - uint32_t workgroud_id_x; - uint32_t workgroud_id_y; - uint32_t workgroud_id_z; + uint32_t workgroup_id_x; + uint32_t workgroup_id_y; + uint32_t workgroup_id_z; uint32_t wave_count; uint64_t timestamp; diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.cpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.cpp index fc4e824403..1259cc96f8 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.cpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.cpp @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.hpp" +#include "pc_record_interface.hpp" uint64_t PCSamplingParserContext::alloc(pcsample_v1_t** buffer, uint64_t size) diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.hpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.hpp index d7ea058193..1cddb80935 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.hpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.hpp @@ -29,8 +29,8 @@ #include #include -#include "lib/rocprofiler-sdk/pc_sampling/parser/correlation.hpp" -#include "lib/rocprofiler-sdk/pc_sampling/parser/parser_types.hpp" +#include "correlation.hpp" +#include "parser_types.h" struct PCSamplingData { diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/rocr.hpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/rocr.h similarity index 93% rename from projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/rocr.hpp rename to projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/rocr.h index 5ba27f12ba..caad7fd341 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/rocr.hpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/rocr.h @@ -22,7 +22,7 @@ #pragma once -#include +#include /** * ######## ROCR Definitions ######## @@ -87,9 +87,9 @@ typedef struct { uint64_t pc; uint64_t exec_mask; - uint32_t workgroud_id_x; - uint32_t workgroud_id_y; - uint32_t workgroud_id_z; + uint32_t workgroup_id_x; + uint32_t workgroup_id_y; + uint32_t workgroup_id_z; uint32_t chiplet_and_wave_id; uint32_t hw_id; reserved_type reserved[3]; @@ -101,9 +101,9 @@ typedef struct { uint64_t pc; uint64_t exec_mask; - uint32_t workgroud_id_x; - uint32_t workgroud_id_y; - uint32_t workgroud_id_z; + uint32_t workgroup_id_x; + uint32_t workgroup_id_y; + uint32_t workgroup_id_z; uint32_t chiplet_and_wave_id; uint32_t hw_id; uint32_t perf_snapshot_data; diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/CMakeLists.txt b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/CMakeLists.txt index af71822253..00f34961ab 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/CMakeLists.txt +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/CMakeLists.txt @@ -2,24 +2,51 @@ rocprofiler_deactivate_clang_tidy() include(GoogleTest) -set(ROCPROFILER_LIB_PC_SAMPLING_PARSER_TEST_SOURCES pcs_parser.cpp) -set(ROCPROFILER_LIB_PC_SAMPLING_PARSER_TEST_HEADERS mocks.hpp) +set(PCTEST_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/source/lib/rocprofiler-sdk/pc_sampling/parser/) -add_executable(pcs-parser-test) +set(ROCPROFILER_LIB_PC_SAMPLING_PARSER_ID_TEST_SOURCES correlation_id_test.cpp) +set(ROCPROFILER_LIB_PC_SAMPLING_PARSER_BENCH_TEST_SOURCES benchmark_test.cpp) +set(ROCPROFILER_LIB_PC_SAMPLING_PARSER_GFX9_TEST_SOURCES gfx9test.cpp) +file(GLOB ROCPROFILER_LIB_PC_SAMPLING_PARSER_TEST_HEADERS mocks.hpp + ${ROCPROFILER_LIB_PC_SAMPLING_PARSER_HEADERS}) -target_sources(pcs-parser-test PRIVATE ${ROCPROFILER_LIB_PC_SAMPLING_PARSER_TEST_SOURCES} - ${ROCPROFILER_LIB_PC_SAMPLING_PARSER_TEST_HEADERS}) -# $) +add_executable(pcs_gfx9_test) -target_link_libraries( - pcs-parser-test - PRIVATE rocprofiler::rocprofiler-common-library - rocprofiler::rocprofiler-static-library GTest::gtest GTest::gtest_main) +target_sources(pcs_gfx9_test + PRIVATE ${ROCPROFILER_LIB_PC_SAMPLING_PARSER_GFX9_TEST_SOURCES}) +target_include_directories(pcs_gfx9_test PRIVATE ${PCTEST_INCLUDE_DIR}) + +target_link_libraries(pcs_gfx9_test PRIVATE GTest::gtest GTest::gtest_main) gtest_add_tests( - TARGET pcs-parser-test - SOURCES ${ROCPROFILER_LIB_COUNTER_TEST_SOURCES} - TEST_LIST pcs-parser-tests_TESTS + TARGET pcs_gfx9_test + SOURCES ${ROCPROFILER_LIB_PC_SAMPLING_PARSER_GFX9_TEST_SOURCES} + TEST_LIST pcs_gfx9_test_TESTS WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) -set_tests_properties(${pcs-parser-tests_TESTS} PROPERTIES TIMEOUT 45 LABELS "unittests") +set_tests_properties(${pcs_gfx9_test_TESTS} PROPERTIES TIMEOUT 45 LABELS "unittests") + +add_executable(pcs_id_test) + +target_sources(pcs_id_test PRIVATE ${ROCPROFILER_LIB_PC_SAMPLING_PARSER_ID_TEST_SOURCES}) +target_include_directories(pcs_id_test PRIVATE ${PCTEST_INCLUDE_DIR}) + +target_link_libraries(pcs_id_test PRIVATE GTest::gtest GTest::gtest_main) + +gtest_add_tests( + TARGET pcs_id_test + SOURCES ${ROCPROFILER_LIB_PC_SAMPLING_PARSER_ID_TEST_SOURCES} + TEST_LIST pcs_id_test_TESTS + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +set_tests_properties(${pcs_id_test_TESTS} PROPERTIES TIMEOUT 45 LABELS "unittests") + +add_executable(pcs_bench_test) + +target_compile_options(pcs_bench_test PRIVATE "-Ofast") +target_sources(pcs_bench_test + PRIVATE ${ROCPROFILER_LIB_PC_SAMPLING_PARSER_BENCH_TEST_SOURCES}) +target_include_directories(pcs_bench_test PRIVATE ${PCTEST_INCLUDE_DIR}) + +target_link_libraries(pcs_bench_test PRIVATE GTest::gtest GTest::gtest_main) diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/benchmark_test.cpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/benchmark_test.cpp new file mode 100644 index 0000000000..34d9131396 --- /dev/null +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/benchmark_test.cpp @@ -0,0 +1,94 @@ +// MIT License +// +// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include +#include + +#include "mocks.hpp" + +#define GFXIP_MAJOR 9 + +/** + * Benchmarks how fast the parser can process samples on a single threaded case + * Current: 5600X with -Ofast, up to >140 million samples/s or ~9GB/s R/W (18GB/s bidirectional) + */ +static bool +Benchmark(bool bWarmup) +{ + constexpr size_t SAMPLE_PER_DISPATCH = 8192; + constexpr size_t DISP_PER_QUEUE = 12; + constexpr size_t NUM_QUEUES = MockDoorBell::num_unique_bells; + + std::shared_ptr buffer = std::make_shared(); + std::array>, NUM_QUEUES> active_dispatches; + + for(size_t q = 0; q < NUM_QUEUES; q++) + { + std::shared_ptr queue = std::make_shared(DISP_PER_QUEUE * 2, buffer); + for(size_t d = 0; d < DISP_PER_QUEUE; d++) + active_dispatches[q].push_back(std::make_shared(queue)); + } + + constexpr size_t TOTAL_NUM_SAMPLES = NUM_QUEUES * DISP_PER_QUEUE * SAMPLE_PER_DISPATCH; + buffer->genUpcomingSamples(TOTAL_NUM_SAMPLES); + + for(auto& queue : active_dispatches) + for(auto& dispatch : queue) + for(size_t i = 0; i < SAMPLE_PER_DISPATCH; i++) + MockWave(dispatch).genPCSample(); + + std::pair userdata; + userdata.first = new pcsample_v1_t[TOTAL_NUM_SAMPLES]; + userdata.second = TOTAL_NUM_SAMPLES; + + auto t0 = std::chrono::system_clock::now(); + CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), + buffer->packets.size(), + GFXIP_MAJOR, + [](pcsample_v1_t** sample, uint64_t size, void* userdata_) { + auto* pair = reinterpret_cast*>( + userdata_); + assert(TOTAL_NUM_SAMPLES == pair->second); + *sample = pair->first; + return size; + }, + &userdata)); + auto t1 = std::chrono::system_clock::now(); + float samples_per_us = float(TOTAL_NUM_SAMPLES) / (t1 - t0).count() * 1E3f; + + if(!bWarmup) + { + std::cout << "Benchmark: Parsed " << int(samples_per_us * 1E3f + 0.5f) * 1E-3f + << " Msample/s ("; + std::cout << int(sizeof(pcsample_v1_t) * samples_per_us) << " MB/s)" << std::endl; + } + + delete[] userdata.first; + return true; +} + +TEST(pcs_parser, benchmark_test) +{ + EXPECT_EQ(Benchmark(true), true); + EXPECT_EQ(Benchmark(false), true); + EXPECT_EQ(Benchmark(false), true); +} diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/correlation_id_test.cpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/correlation_id_test.cpp new file mode 100644 index 0000000000..1245e64986 --- /dev/null +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/correlation_id_test.cpp @@ -0,0 +1,351 @@ +// MIT License +// +// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include +#include + +#include "mocks.hpp" +#include "pc_record_interface.hpp" + +#define GFXIP_MAJOR 9 + +std::mt19937 rdgen(1); + +/** + * Sample user memory allocation callback. + * It expects userdata to be cast-able to a pointer to + * std::vector> + */ +static uint64_t +alloc_callback(pcsample_v1_t** buffer, uint64_t size, void* userdata) +{ + *buffer = new pcsample_v1_t[size]; + auto& vector = *reinterpret_cast>*>(userdata); + vector.push_back({*buffer, size}); + return size; +} + +/** + * Uses the MockWave dispatch's unique_id store in the pc field to verify + * the reconstructed correlation_id. + */ +static bool +check_samples(pcsample_v1_t* samples, uint64_t size) +{ + for(size_t i = 0; i < size; i++) + if(samples[i].correlation_id != samples[i].pc) return false; + return true; +} + +/** + * Simplest mock classes use, generates a single queue+dispatch with 2 PC samples. + */ +TEST(pcs_parser, hello_world) +{ + std::shared_ptr buffer = std::make_shared(); + std::shared_ptr queue = std::make_shared(16, buffer); + std::shared_ptr dispatch = std::make_shared(queue); + + buffer->genUpcomingSamples(2); + MockWave(dispatch).genPCSample(); + MockWave(dispatch).genPCSample(); + + std::vector> all_allocations; + + CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), + buffer->packets.size(), + GFXIP_MAJOR, + alloc_callback, + (void*) &all_allocations)); + + EXPECT_EQ(all_allocations.size(), 1); // HelloWorld: Incorrect number of callbacks + for(auto& sample : all_allocations) + { + EXPECT_EQ(sample.second, 2); // HelloWorld: Incorrect number of samples + EXPECT_EQ(check_samples(sample.first, sample.second), + true); // HelloWorld: parsed ID does not match correct ID + delete[] sample.first; + } +} + +/** + * A little more complicated. + * Generates a few dispatches for 2 different queues and samples in forward and reverse order. + * Checks if the reconstructed correlation_id is correct. + */ +TEST(pcs_parser, reverse_wave_order) +{ + std::shared_ptr buffer = std::make_shared(); + std::shared_ptr queue1 = std::make_shared(16, buffer); + std::shared_ptr queue2 = std::make_shared(16, buffer); + + std::vector> dispatches; + dispatches.push_back(std::make_shared(queue1)); + dispatches.push_back(std::make_shared(queue1)); + dispatches.push_back(std::make_shared(queue2)); + dispatches.push_back(std::make_shared(queue2)); + dispatches.push_back(std::make_shared(queue1)); + + buffer->genUpcomingSamples(dispatches.size()); + for(auto it = dispatches.rbegin(); it != dispatches.rend(); it++) + MockWave(*it).genPCSample(); + buffer->genUpcomingSamples(dispatches.size()); + for(auto it = dispatches.begin(); it != dispatches.end(); it++) + MockWave(*it).genPCSample(); + + std::vector> all_allocations; + + CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), + buffer->packets.size(), + GFXIP_MAJOR, + alloc_callback, + (void*) &all_allocations)); + + EXPECT_EQ(all_allocations.size(), 2); // ReverseWaveOrder test: Incorrect number of callbacks + for(auto& sample : all_allocations) + { + EXPECT_EQ(sample.second, + dispatches.size()); // ReverseWaveOrder: Incorrect number of samples + EXPECT_EQ(check_samples(sample.first, sample.second), + true); // ReverseWaveOrder: parsed ID does not match correct ID + delete[] sample.first; + } +} + +/** + * Creates a small queue and causes the dispatch_ids to wrap around a few times, and generates + * a single sample per dispatch. Checks the parser is properly handling the wrapping of queues. + */ +TEST(pcs_parser, dispatch_wrapping) +{ + const int num_samples = 32; + std::shared_ptr buffer = std::make_shared(); + std::shared_ptr queue = std::make_shared(5, buffer); + + for(int i = 0; i < num_samples; i++) + { + auto dispatch = std::make_shared(queue); + buffer->genUpcomingSamples(1); + MockWave(dispatch).genPCSample(); + } + + std::vector> all_allocations; + + CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), + buffer->packets.size(), + GFXIP_MAJOR, + alloc_callback, + (void*) &all_allocations)); + + EXPECT_EQ(all_allocations.size(), + num_samples); // RandomSamples test: Incorrect number of callbacks + for(auto& sample : all_allocations) + { + EXPECT_EQ(sample.second, 1); // RandomSamples: Incorrect number of samples + EXPECT_EQ(check_samples(sample.first, sample.second), + true); // RandomSamples: parsed ID does not match correct ID + delete[] sample.first; + } +} + +/** + * Creates a few queues with a few dispatchs per queue. + * Adds random samples per dispatch, and checks the result. + */ +TEST(pcs_parser, random_samples) +{ + const int num_samples = 1024; + std::shared_ptr buffer = std::make_shared(); + std::shared_ptr queue1 = std::make_shared(16, buffer); + std::shared_ptr queue2 = std::make_shared(16, buffer); + std::shared_ptr queue3 = std::make_shared(16, buffer); + std::shared_ptr queue4 = std::make_shared(16, buffer); + + std::vector> dispatches; + dispatches.push_back(std::make_shared(queue1)); + dispatches.push_back(std::make_shared(queue1)); + dispatches.push_back(std::make_shared(queue2)); + dispatches.push_back(std::make_shared(queue3)); + dispatches.push_back(std::make_shared(queue1)); + dispatches.push_back(std::make_shared(queue3)); + dispatches.push_back(std::make_shared(queue3)); + dispatches.push_back(std::make_shared(queue2)); + dispatches.push_back(std::make_shared(queue1)); + + buffer->genUpcomingSamples(num_samples); + for(int i = 0; i < num_samples; i++) + MockWave(dispatches[rdgen() % dispatches.size()]).genPCSample(); + + std::vector> all_allocations; + + CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), + buffer->packets.size(), + GFXIP_MAJOR, + alloc_callback, + (void*) &all_allocations)); + + EXPECT_EQ(all_allocations.size(), 1); // RandomSamples test: Incorrect number of callbacks + for(auto& sample : all_allocations) + { + EXPECT_EQ(sample.second, num_samples); // RandomSamples: Incorrect number of samples + EXPECT_EQ(check_samples(sample.first, sample.second), + true); // RandomSamples: parsed ID does not match correct ID + delete[] sample.first; + } +} + +/** + * Hammers the parser by creating and destrying queues at random, adding dispatches at random + * and generating PC samples at random. By default we use all 4 unique doorbells, + * queue size is 16 and we generate 10k samples dispatch. + */ +TEST(pcs_parser, queue_hammer) +{ + constexpr int NUM_ACTIONS = 10000; + constexpr int QSIZE = 16; + constexpr int NUM_QUEUES = MockDoorBell::num_unique_bells; + constexpr int ACTION_MAX = QSIZE * NUM_QUEUES / 2; + + std::shared_ptr buffer = std::make_shared(); + + std::array, NUM_QUEUES> queues; + std::array>, NUM_QUEUES> active_dispatches; + + int num_reset_queues = 0; + int num_samples_generated = 0; + int num_dispatches_generated = 0; + double avg_q_occupancy = 0; + size_t max_q_occupancy = 0; + + for(int i = 0; i < NUM_QUEUES; i++) + queues[i] = std::make_shared(QSIZE, buffer); + for(int i = 0; i < NUM_QUEUES; i++) + active_dispatches[i].push_back(std::make_shared(queues[i])); + + for(int i = 0; i < NUM_ACTIONS; i++) + { + int q = rdgen() % NUM_QUEUES; + int action = rdgen() % ACTION_MAX; + if(action == 0) + { + // Delete queue and create new one + active_dispatches[q] = {}; + queues[q].reset(); + queues[q] = std::make_shared(QSIZE, buffer); + num_reset_queues++; + } + else if(action > ACTION_MAX / 2 && active_dispatches[q].size() > 1) + { + // Delete dispatch + active_dispatches[q].erase(active_dispatches[q].begin(), + active_dispatches[q].begin() + 1); + } + + // Add new dispatch + if(active_dispatches[q].size() < QSIZE) + { + active_dispatches[q].push_back(std::make_shared(queues[q])); + num_dispatches_generated += 1; + } + + // Generate one "pc" sample for each queue + buffer->genUpcomingSamples(NUM_QUEUES); + for(auto& queue : active_dispatches) + { + EXPECT_NE(queue.size(), 0); + std::shared_ptr rand_dispatch = queue[rdgen() % queue.size()]; + MockWave(rand_dispatch).genPCSample(); + num_samples_generated += 1; + avg_q_occupancy += queue.size(); + max_q_occupancy = std::max(max_q_occupancy, queue.size()); + } + } + + std::cout << "Hammer Stats: " << std::endl; + std::cout << "num_reset_queues: " << num_reset_queues << std::endl; + std::cout << "num_samples_generated: " << num_samples_generated << std::endl; + std::cout << "num_dispatches_generated: " << num_dispatches_generated << std::endl; + std::cout << "Avg queue occupancy: " << avg_q_occupancy / (NUM_ACTIONS * NUM_QUEUES) + << std::endl; + std::cout << "Max queue occupancy: " << max_q_occupancy << "\n\n" << std::endl; + + std::vector> all_allocations; + + CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), + buffer->packets.size(), + GFXIP_MAJOR, + alloc_callback, + (void*) &all_allocations)); + + EXPECT_EQ(all_allocations.size(), + NUM_ACTIONS); // QueueHammer test: Incorrect number of callbacks + for(auto sb = 0ul; sb < all_allocations.size(); sb++) + { + pcsample_v1_t* samples = all_allocations[sb].first; + size_t num_samples = all_allocations[sb].second; + + EXPECT_EQ(num_samples, NUM_QUEUES); // QueueHammer: Incorrect number of samples + EXPECT_EQ(check_samples(samples, num_samples), + true); // QueueHammer: parsed ID does not match correct ID + delete[] samples; + } +} + +TEST(pcs_parser, multi_buffer) +{ + std::shared_ptr firstBuffer = std::make_shared(); + std::shared_ptr queue = std::make_shared(16, firstBuffer); + std::shared_ptr dispatch1 = std::make_shared(queue); + std::shared_ptr dispatch2 = std::make_shared(queue); + + firstBuffer->genUpcomingSamples(4); + MockWave(dispatch1).genPCSample(); + MockWave(dispatch2).genPCSample(); + MockWave(dispatch1).genPCSample(); + MockWave(dispatch2).genPCSample(); + + std::shared_ptr secondBuffer = std::make_shared(); + const auto& packets = firstBuffer->packets; + secondBuffer->packets = std::vector(packets.begin() + 2, packets.end()); + + std::vector> all_allocations; + + CHECK_PARSER(parse_buffer((generic_sample_t*) firstBuffer->packets.data(), + firstBuffer->packets.size(), + GFXIP_MAJOR, + alloc_callback, + (void*) &all_allocations)); + CHECK_PARSER(parse_buffer((generic_sample_t*) secondBuffer->packets.data(), + secondBuffer->packets.size(), + GFXIP_MAJOR, + alloc_callback, + (void*) &all_allocations)); + + EXPECT_EQ(all_allocations.size(), 2); // MultiBuffer: Incorrect number of callbacks + auto& sample = all_allocations[1]; + EXPECT_EQ(sample.second, 4); // MultiBuffer: Incorrect number of samples + EXPECT_EQ(check_samples(sample.first, sample.second), + true); // MultiBuffer: parsed ID does not match correct ID + + delete[] all_allocations[0].first; + delete[] all_allocations[1].first; +}; diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/gfx9test.cpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/gfx9test.cpp new file mode 100644 index 0000000000..98a2c9a09c --- /dev/null +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/gfx9test.cpp @@ -0,0 +1,407 @@ +// MIT License +// +// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifdef NDEBUG +# undef NDEBUG +#endif + +#include +#include +#include + +#include "mocks.hpp" +#include "pc_record_interface.hpp" + +#define GFXIP_MAJOR 9 + +#define TYPECHECK(x) \ + snapshots.push_back(pcsample_snapshot_v1_t{.dual_issue_valu = 0, \ + .inst_type = ::PCSAMPLE::x, \ + .reason_not_issued = 0, \ + .arb_state_issue = 0, \ + .arb_state_stall = 0}); +#define UNROLL_TYPECHECK() \ + TYPECHECK(TYPE_VALU); \ + TYPECHECK(TYPE_MATRIX); \ + TYPECHECK(TYPE_SCALAR); \ + TYPECHECK(TYPE_TEX); \ + TYPECHECK(TYPE_LDS); \ + TYPECHECK(TYPE_FLAT); \ + TYPECHECK(TYPE_EXP); \ + TYPECHECK(TYPE_MESSAGE); \ + TYPECHECK(TYPE_BARRIER); \ + TYPECHECK(TYPE_BRANCH_NOT_TAKEN); \ + TYPECHECK(TYPE_BRANCH_TAKEN); \ + TYPECHECK(TYPE_JUMP); \ + TYPECHECK(TYPE_OTHER); \ + TYPECHECK(TYPE_NO_INST); + +#define REASONCHECK(x) \ + snapshots.push_back(pcsample_snapshot_v1_t{.dual_issue_valu = 0, \ + .inst_type = 0, \ + .reason_not_issued = ::PCSAMPLE::x, \ + .arb_state_issue = 0, \ + .arb_state_stall = 0}); +#define UNROLL_REASONCHECK(x) \ + REASONCHECK(REASON_NOT_AVAILABLE); \ + REASONCHECK(REASON_ALU); \ + REASONCHECK(REASON_WAITCNT); \ + REASONCHECK(REASON_INTERNAL); \ + REASONCHECK(REASON_BARRIER); \ + REASONCHECK(REASON_ARBITER); \ + REASONCHECK(REASON_EX_STALL); \ + REASONCHECK(REASON_OTHER_WAIT); + +#define ARBCHECK1(x, y) \ + snapshots.push_back(pcsample_snapshot_v1_t{.dual_issue_valu = 0, \ + .inst_type = 0, \ + .reason_not_issued = 0, \ + .arb_state_issue = 1 << ::PCSAMPLE::x, \ + .arb_state_stall = 1 << ::PCSAMPLE::y}); +#define ARBCHECK2(x) \ + ARBCHECK1(x, ISSUE_VALU); \ + ARBCHECK1(x, ISSUE_MATRIX); \ + ARBCHECK1(x, ISSUE_SCALAR); \ + ARBCHECK1(x, ISSUE_VMEM_TEX); \ + ARBCHECK1(x, ISSUE_LDS); \ + ARBCHECK1(x, ISSUE_FLAT); \ + ARBCHECK1(x, ISSUE_EXP); \ + ARBCHECK1(x, ISSUE_MISC); + +#define UNROLL_ARBCHECK() \ + ARBCHECK2(ISSUE_VALU); \ + ARBCHECK2(ISSUE_MATRIX); \ + ARBCHECK2(ISSUE_SCALAR); \ + ARBCHECK2(ISSUE_VMEM_TEX); \ + ARBCHECK2(ISSUE_LDS); \ + ARBCHECK2(ISSUE_FLAT); \ + ARBCHECK2(ISSUE_EXP); \ + ARBCHECK2(ISSUE_MISC); + +class WaveSnapTest +{ +public: + WaveSnapTest() + { + buffer = std::make_shared(); + queue = std::make_shared(16, buffer); + dispatch = std::make_shared(queue); + } + + void Test() + { + FillBuffers(); + CheckBuffers(); + } + + virtual void FillBuffers() = 0; + virtual void CheckBuffers() = 0; + + void genPCSample(int wave_cnt, int inst_type, int reason, int arb_issue, int arb_stall) + { + wave_cnt &= 0x3F; + inst_type &= 0xF; + reason &= 0x7; + arb_issue &= 0xFF; + arb_stall &= 0xFF; + + perf_sample_snapshot_v1 snap; + ::memset(&snap, 0, sizeof(snap)); + snap.pc = dispatch->unique_id; + snap.correlation_id = dispatch->getMockId(); + + snap.perf_snapshot_data = (inst_type << 3) | (reason << 7); + snap.perf_snapshot_data |= (arb_issue << 10) | (arb_stall << 18); + snap.perf_snapshot_data1 = wave_cnt; + + assert(dispatch.get()); + dispatch->submit(packet_union_t{.snap = snap}); + }; + + std::shared_ptr buffer; + std::shared_ptr queue; + std::shared_ptr dispatch; +}; + +class WaveCntTest : public WaveSnapTest +{ +public: + void FillBuffers() override + { + // Loop over all possible wave_cnt + buffer->genUpcomingSamples(max_wave_number); + for(size_t i = 0; i < max_wave_number; i++) + genPCSample(i, GFX9::TYPE_LDS, GFX9::REASON_ALU, GFX9::ISSUE_VALU, GFX9::ISSUE_VALU); + } + + void CheckBuffers() override + { + auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 + assert(parsed.size() == 1); + assert(parsed[0].size() == max_wave_number); + + for(size_t i = 0; i < max_wave_number; i++) + assert(parsed[0][i].wave_count == i); + } + + const size_t max_wave_number = 64; + std::vector snapshots; +}; + +class InstTypeTest : public WaveSnapTest +{ +public: + void FillBuffers() override + { + // Loop over inst_type_issued + UNROLL_TYPECHECK(); + buffer->genUpcomingSamples(GFX9::TYPE_LAST); + for(int i = 0; i < GFX9::TYPE_LAST; i++) + genPCSample(i, i, GFX9::REASON_ALU, GFX9::ISSUE_MATRIX, GFX9::ISSUE_MATRIX); + } + + void CheckBuffers() override + { + auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 + assert(parsed.size() == 1); + assert(parsed[0].size() == GFX9::TYPE_LAST); + assert(snapshots.size() == GFX9::TYPE_LAST); + + for(size_t i = 0; i < GFX9::TYPE_LAST; i++) + assert(snapshots[i].inst_type == parsed[0][i].snapshot.inst_type); + } + + std::vector snapshots; +}; + +class StallReasonTest : public WaveSnapTest +{ +public: + void FillBuffers() override + { + // Loop over reason_not_issued + UNROLL_REASONCHECK(); + buffer->genUpcomingSamples(GFX9::REASON_LAST); + for(int i = 0; i < GFX9::REASON_LAST; i++) + genPCSample(i, GFX9::TYPE_MATRIX, i, GFX9::ISSUE_MATRIX, GFX9::ISSUE_MATRIX); + } + + void CheckBuffers() override + { + auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 + assert(parsed.size() == 1); + assert(parsed[0].size() == GFX9::REASON_LAST); + assert(snapshots.size() == GFX9::REASON_LAST); + + for(size_t i = 0; i < GFX9::REASON_LAST; i++) + assert(snapshots[i].reason_not_issued == parsed[0][i].snapshot.reason_not_issued); + } + + std::vector snapshots; +}; + +class ArbStateTest : public WaveSnapTest +{ +public: + void FillBuffers() override + { + // Loop over arb_state_issue + UNROLL_ARBCHECK(); + buffer->genUpcomingSamples(GFX9::ISSUE_LAST * GFX9::ISSUE_LAST); + for(int i = 0; i < GFX9::ISSUE_LAST; i++) + for(int j = 0; j < GFX9::ISSUE_LAST; j++) + genPCSample(i, GFX9::TYPE_MATRIX, GFX9::REASON_ALU, 1 << i, 1 << j); + } + + void CheckBuffers() override + { + auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 + assert(parsed.size() == 1); + assert(parsed[0].size() == GFX9::ISSUE_LAST * GFX9::ISSUE_LAST); + assert(snapshots.size() == GFX9::ISSUE_LAST * GFX9::ISSUE_LAST); + + for(size_t i = 0; i < GFX9::ISSUE_LAST * GFX9::ISSUE_LAST; i++) + { + auto& snap = snapshots[i]; + assert(snap.arb_state_issue == parsed[0][i].snapshot.arb_state_issue); + assert(snap.arb_state_stall == parsed[0][i].snapshot.arb_state_stall); + } + } + + std::vector snapshots; +}; + +class WaveIssueAndErrorTest : public WaveSnapTest +{ + void FillBuffers() override + { + buffer->genUpcomingSamples(16); + for(int valid = 0; valid <= 1; valid++) + for(int issued = 0; issued <= 1; issued++) + for(int dual = 0; dual <= 1; dual++) + for(int error = 0; error <= 1; error++) + genPCSample(valid, issued, dual, error); + } + + void CheckBuffers() override + { + const int num_combinations = 16; + auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 + assert(parsed.size() == 1); + assert(parsed[0].size() == num_combinations); + assert(compare.size() == num_combinations); + + for(size_t i = 0; i < num_combinations; i++) + { + assert(compare[i].flags.valid == parsed[0][i].flags.valid); + assert(compare[i].wave_issued == parsed[0][i].wave_issued); + assert(compare[i].snapshot.dual_issue_valu == parsed[0][i].snapshot.dual_issue_valu); + } + } + + union trap_snapshot_v1 + { + struct + { + uint32_t valid : 1; + uint32_t issued : 1; + uint32_t dual : 1; + uint32_t reserved : 23; + uint32_t error : 1; + uint32_t reserved2 : 5; + }; + uint32_t raw; + }; + + void genPCSample(bool valid, bool issued, bool dual, bool error) + { + pcsample_v1_t sample; + ::memset(&sample, 0, sizeof(sample)); + sample.pc = dispatch->unique_id; + sample.correlation_id = dispatch->getMockId(); + + sample.flags.valid = valid && !error; + sample.wave_issued = issued; + sample.snapshot.dual_issue_valu = dual; + + assert(dispatch.get()); + + compare.push_back(sample); + + trap_snapshot_v1 snap; + snap.valid = valid; + snap.issued = issued; + snap.dual = dual; + snap.error = error; + + perf_sample_snapshot_v1 pss; + pss.perf_snapshot_data = snap.raw; + pss.correlation_id = dispatch->getMockId(); + dispatch->submit(std::move(pss)); + }; + + std::vector compare; +}; + +class WaveOtherFieldsTest : public WaveSnapTest +{ + void FillBuffers() override + { + buffer->genUpcomingSamples(3); + genPCSample(1, 2, 3, 4, 5, 6, 7, 8); // Counting + genPCSample(3, 5, 7, 11, 13, 17, 19, 23); // Some prime numbers + genPCSample(23, 19, 17, 13, 11, 7, 5, 3); // Some reversed primes + } + + void CheckBuffers() override + { + auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 + assert(parsed.size() == 1); + assert(parsed[0].size() == 3); + assert(compare.size() == 3); + + for(size_t i = 0; i < 3; i++) + { + assert(parsed[0][i].flags.has_stall_reason == true); + assert(parsed[0][i].flags.has_wave_cnt == true); + assert(parsed[0][i].flags.has_memory_counter == false); + + assert(compare[i].exec_mask == parsed[0][i].exec_mask); + assert(compare[i].workgroup_id_x == parsed[0][i].workgroup_id_x); + assert(compare[i].workgroup_id_y == parsed[0][i].workgroup_id_y); + assert(compare[i].workgroup_id_z == parsed[0][i].workgroup_id_z); + + assert(compare[i].chiplet == parsed[0][i].chiplet); + assert(compare[i].wave_id == parsed[0][i].wave_id); + assert(compare[i].hw_id == parsed[0][i].hw_id); + assert(compare[i].correlation_id == parsed[0][i].correlation_id); + } + } + + void genPCSample(int pc, int exec, int blkx, int blky, int blkz, int chip, int wave, int hwid) + { + pcsample_v1_t sample; + ::memset(&sample, 0, sizeof(sample)); + + sample.exec_mask = exec; + sample.workgroup_id_x = blkx; + sample.workgroup_id_y = blky; + sample.workgroup_id_z = blkz; + + sample.chiplet = chip; + sample.wave_id = wave; + sample.hw_id = hwid; + sample.correlation_id = dispatch->unique_id; + + compare.push_back(sample); + + perf_sample_snapshot_v1 snap; + ::memset(&snap, 0, sizeof(snap)); + snap.exec_mask = exec; + + snap.workgroup_id_x = blkx; + snap.workgroup_id_y = blky; + snap.workgroup_id_z = blkz; + snap.chiplet_and_wave_id = (chip << 8) | (wave & 0x3F); + snap.hw_id = hwid; + snap.correlation_id = dispatch->getMockId(); + + assert(dispatch.get()); + dispatch->submit(snap); + + (void) pc; + }; + + std::vector compare; +}; + +TEST(pcs_parser, gfx9_test) +{ + WaveCntTest{}.Test(); + InstTypeTest{}.Test(); + StallReasonTest{}.Test(); + ArbStateTest{}.Test(); + WaveIssueAndErrorTest{}.Test(); + WaveOtherFieldsTest{}.Test(); + + std::cout << "GFX9 Test Done." << std::endl; +} diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/mocks.hpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/mocks.hpp index a0579af6a2..77c25de236 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/mocks.hpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/mocks.hpp @@ -31,7 +31,7 @@ #include #include -#include "lib/rocprofiler-sdk/pc_sampling/parser/correlation.hpp" +#include "correlation.hpp" #define CHECK_PARSER(x) \ { \ @@ -39,7 +39,7 @@ if(val != PCSAMPLE_STATUS_SUCCESS) \ { \ std::cerr << __FILE__ << ':' << __LINE__ << " Parser error: " << val << std::endl; \ - exit(1); \ + exit(val); \ } \ } diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/pcs_parser.cpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/pcs_parser.cpp deleted file mode 100644 index ec1ac93d46..0000000000 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/tests/pcs_parser.cpp +++ /dev/null @@ -1,805 +0,0 @@ -// MIT License -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifdef NDEBUG -# undef NDEBUG -#endif - -#include -#include -#include - -#include "lib/rocprofiler-sdk/pc_sampling/parser/pc_record_interface.hpp" -#include "lib/rocprofiler-sdk/pc_sampling/parser/tests/mocks.hpp" - -#define GFXIP_MAJOR 9 - -#define TYPECHECK(x) \ - snapshots.push_back(pcsample_snapshot_v1_t{.dual_issue_valu = 0, \ - .inst_type = ::PCSAMPLE::x, \ - .reason_not_issued = 0, \ - .arb_state_issue = 0, \ - .arb_state_stall = 0}); -#define UNROLL_TYPECHECK() \ - TYPECHECK(TYPE_VALU); \ - TYPECHECK(TYPE_MATRIX); \ - TYPECHECK(TYPE_SCALAR); \ - TYPECHECK(TYPE_TEX); \ - TYPECHECK(TYPE_LDS); \ - TYPECHECK(TYPE_FLAT); \ - TYPECHECK(TYPE_EXP); \ - TYPECHECK(TYPE_MESSAGE); \ - TYPECHECK(TYPE_BARRIER); \ - TYPECHECK(TYPE_BRANCH_NOT_TAKEN); \ - TYPECHECK(TYPE_BRANCH_TAKEN); \ - TYPECHECK(TYPE_JUMP); \ - TYPECHECK(TYPE_OTHER); \ - TYPECHECK(TYPE_NO_INST); - -#define REASONCHECK(x) \ - snapshots.push_back(pcsample_snapshot_v1_t{.dual_issue_valu = 0, \ - .inst_type = 0, \ - .reason_not_issued = ::PCSAMPLE::x, \ - .arb_state_issue = 0, \ - .arb_state_stall = 0}); -#define UNROLL_REASONCHECK(x) \ - REASONCHECK(REASON_NOT_AVAILABLE); \ - REASONCHECK(REASON_ALU); \ - REASONCHECK(REASON_WAITCNT); \ - REASONCHECK(REASON_INTERNAL); \ - REASONCHECK(REASON_BARRIER); \ - REASONCHECK(REASON_ARBITER); \ - REASONCHECK(REASON_EX_STALL); \ - REASONCHECK(REASON_OTHER_WAIT); - -#define ARBCHECK1(x, y) \ - snapshots.push_back(pcsample_snapshot_v1_t{.dual_issue_valu = 0, \ - .inst_type = 0, \ - .reason_not_issued = 0, \ - .arb_state_issue = 1 << ::PCSAMPLE::x, \ - .arb_state_stall = 1 << ::PCSAMPLE::y}); -#define ARBCHECK2(x) \ - ARBCHECK1(x, ISSUE_VALU); \ - ARBCHECK1(x, ISSUE_MATRIX); \ - ARBCHECK1(x, ISSUE_SCALAR); \ - ARBCHECK1(x, ISSUE_VMEM_TEX); \ - ARBCHECK1(x, ISSUE_LDS); \ - ARBCHECK1(x, ISSUE_FLAT); \ - ARBCHECK1(x, ISSUE_EXP); \ - ARBCHECK1(x, ISSUE_MISC); - -#define UNROLL_ARBCHECK() \ - ARBCHECK2(ISSUE_VALU); \ - ARBCHECK2(ISSUE_MATRIX); \ - ARBCHECK2(ISSUE_SCALAR); \ - ARBCHECK2(ISSUE_VMEM_TEX); \ - ARBCHECK2(ISSUE_LDS); \ - ARBCHECK2(ISSUE_FLAT); \ - ARBCHECK2(ISSUE_EXP); \ - ARBCHECK2(ISSUE_MISC); - -std::mt19937 rdgen(1); - -TEST(pcs_parser_context, init) { PCSamplingParserContext context; } - -/** - * Sample user memory allocation callback. - * It expects userdata to be cast-able to a pointer to - * std::vector> - */ -static uint64_t -alloc_callback(pcsample_v1_t** buffer, uint64_t size, void* userdata) -{ - *buffer = new pcsample_v1_t[size]; - auto& vector = *reinterpret_cast>*>(userdata); - vector.push_back({*buffer, size}); - return size; -} - -/** - * Uses the MockWave dispatch's unique_id store in the pc field to verify - * the reconstructed correlation_id. - */ -static bool -check_samples(pcsample_v1_t* samples, uint64_t size) -{ - for(size_t i = 0; i < size; i++) - if(samples[i].correlation_id != samples[i].pc) return false; - return true; -} - -/** - * Simplest mock classes use, generates a single queue+dispatch with 2 PC samples. - */ -TEST(pcs_parser_correlation_id, hello_world) -{ - std::shared_ptr buffer = std::make_shared(); - std::shared_ptr queue = std::make_shared(16, buffer); - std::shared_ptr dispatch = std::make_shared(queue); - - buffer->genUpcomingSamples(2); - MockWave(dispatch).genPCSample(); - MockWave(dispatch).genPCSample(); - - std::vector> all_allocations; - - CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), - buffer->packets.size(), - GFXIP_MAJOR, - alloc_callback, - (void*) &all_allocations)); - - assert(all_allocations.size() == 1 && "HelloWorld: Incorrect number of callbacks"); - for(auto& sample : all_allocations) - { - assert(sample.second == 2 && "HelloWorld: Incorrect number of samples"); - assert(check_samples(sample.first, sample.second) && - "HelloWorld: parsed ID does not match correct ID"); - delete[] sample.first; - } -} - -/** - * A little more complicated. - * Generates a few dispatches for 2 different queues and samples in forward and reverse order. - * Checks if the reconstructed correlation_id is correct. - */ -TEST(pcs_parser_correlation_id, reverse_wave_order) -{ - std::shared_ptr buffer = std::make_shared(); - std::shared_ptr queue1 = std::make_shared(16, buffer); - std::shared_ptr queue2 = std::make_shared(16, buffer); - - std::vector> dispatches; - dispatches.push_back(std::make_shared(queue1)); - dispatches.push_back(std::make_shared(queue1)); - dispatches.push_back(std::make_shared(queue2)); - dispatches.push_back(std::make_shared(queue2)); - dispatches.push_back(std::make_shared(queue1)); - - buffer->genUpcomingSamples(dispatches.size()); - for(auto it = dispatches.rbegin(); it != dispatches.rend(); it++) - MockWave(*it).genPCSample(); - buffer->genUpcomingSamples(dispatches.size()); - for(auto it = dispatches.begin(); it != dispatches.end(); it++) - MockWave(*it).genPCSample(); - - std::vector> all_allocations; - - CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), - buffer->packets.size(), - GFXIP_MAJOR, - alloc_callback, - (void*) &all_allocations)); - - assert(all_allocations.size() == 2 && "ReverseWaveOrder test: Incorrect number of callbacks"); - for(auto& sample : all_allocations) - { - assert(sample.second == dispatches.size() && - "ReverseWaveOrder: Incorrect number of samples"); - assert(check_samples(sample.first, sample.second) && - "ReverseWaveOrder: parsed ID does not match correct ID"); - delete[] sample.first; - } -} - -/** - * Creates a small queue and causes the dispatch_ids to wrap around a few times, and generates - * a single sample per dispatch. Checks the parser is properly handling the wrapping of queues. - */ -TEST(pcs_parser_correlation_id, dispatch_wrapping) -{ - const int num_samples = 32; - std::shared_ptr buffer = std::make_shared(); - std::shared_ptr queue = std::make_shared(5, buffer); - - for(int i = 0; i < num_samples; i++) - { - auto dispatch = std::make_shared(queue); - buffer->genUpcomingSamples(1); - MockWave(dispatch).genPCSample(); - } - - std::vector> all_allocations; - - CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), - buffer->packets.size(), - GFXIP_MAJOR, - alloc_callback, - (void*) &all_allocations)); - - assert(all_allocations.size() == num_samples && - "RandomSamples test: Incorrect number of callbacks"); - for(auto& sample : all_allocations) - { - assert(sample.second == 1 && "RandomSamples: Incorrect number of samples"); - assert(check_samples(sample.first, sample.second) && - "RandomSamples: parsed ID does not match correct ID"); - delete[] sample.first; - } -} - -/** - * Creates a few queues with a few dispatchs per queue. - * Adds random samples per dispatch, and checks the result. - */ -TEST(pcs_parser_correlation_id, random_samples) -{ - const int num_samples = 1024; - std::shared_ptr buffer = std::make_shared(); - std::shared_ptr queue1 = std::make_shared(16, buffer); - std::shared_ptr queue2 = std::make_shared(16, buffer); - std::shared_ptr queue3 = std::make_shared(16, buffer); - std::shared_ptr queue4 = std::make_shared(16, buffer); - - std::vector> dispatches; - dispatches.push_back(std::make_shared(queue1)); - dispatches.push_back(std::make_shared(queue1)); - dispatches.push_back(std::make_shared(queue2)); - dispatches.push_back(std::make_shared(queue3)); - dispatches.push_back(std::make_shared(queue1)); - dispatches.push_back(std::make_shared(queue3)); - dispatches.push_back(std::make_shared(queue3)); - dispatches.push_back(std::make_shared(queue2)); - dispatches.push_back(std::make_shared(queue1)); - - buffer->genUpcomingSamples(num_samples); - for(int i = 0; i < num_samples; i++) - MockWave(dispatches[rdgen() % dispatches.size()]).genPCSample(); - - std::vector> all_allocations; - - CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), - buffer->packets.size(), - GFXIP_MAJOR, - alloc_callback, - (void*) &all_allocations)); - - assert(all_allocations.size() == 1 && "RandomSamples test: Incorrect number of callbacks"); - for(auto& sample : all_allocations) - { - assert(sample.second == num_samples && "RandomSamples: Incorrect number of samples"); - assert(check_samples(sample.first, sample.second) && - "RandomSamples: parsed ID does not match correct ID"); - delete[] sample.first; - } -} - -/** - * Hammers the parser by creating and destrying queues at random, adding dispatches at random - * and generating PC samples at random. By default we use all 4 unique doorbells, - * queue size is 16 and we generate 10k samples dispatch. - */ -TEST(pcs_parser_correlation_id, queue_hammer) -{ - constexpr int NUM_ACTIONS = 10000; - constexpr int QSIZE = 16; - constexpr int NUM_QUEUES = MockDoorBell::num_unique_bells; - constexpr int ACTION_MAX = QSIZE * NUM_QUEUES / 2; - - std::shared_ptr buffer = std::make_shared(); - - std::array, NUM_QUEUES> queues; - std::array>, NUM_QUEUES> active_dispatches; - - int num_reset_queues = 0; - int num_samples_generated = 0; - int num_dispatches_generated = 0; - double avg_q_occupancy = 0; - size_t max_q_occupancy = 0; - - for(int i = 0; i < NUM_QUEUES; i++) - queues[i] = std::make_shared(QSIZE, buffer); - for(int i = 0; i < NUM_QUEUES; i++) - active_dispatches[i].push_back(std::make_shared(queues[i])); - - for(int i = 0; i < NUM_ACTIONS; i++) - { - int q = rdgen() % NUM_QUEUES; - int action = rdgen() % ACTION_MAX; - if(action == 0) - { - // Delete queue and create new one - active_dispatches[q] = {}; - queues[q].reset(); - queues[q] = std::make_shared(QSIZE, buffer); - num_reset_queues++; - } - else if(action > ACTION_MAX / 2 && active_dispatches[q].size() > 1) - { - // Delete dispatch - active_dispatches[q].erase(active_dispatches[q].begin(), - active_dispatches[q].begin() + 1); - } - - // Add new dispatch - if(active_dispatches[q].size() < QSIZE) - { - active_dispatches[q].push_back(std::make_shared(queues[q])); - num_dispatches_generated += 1; - } - - // Generate one "pc" sample for each queue - buffer->genUpcomingSamples(NUM_QUEUES); - for(auto& queue : active_dispatches) - { - assert(queue.size() > 0); - std::shared_ptr rand_dispatch = queue[rdgen() % queue.size()]; - MockWave(rand_dispatch).genPCSample(); - num_samples_generated += 1; - avg_q_occupancy += queue.size(); - max_q_occupancy = std::max(max_q_occupancy, queue.size()); - } - } - - std::cout << "Hammer Stats: " << std::endl; - std::cout << "num_reset_queues: " << num_reset_queues << std::endl; - std::cout << "num_samples_generated: " << num_samples_generated << std::endl; - std::cout << "num_dispatches_generated: " << num_dispatches_generated << std::endl; - std::cout << "Avg queue occupancy: " << avg_q_occupancy / (NUM_ACTIONS * NUM_QUEUES) - << std::endl; - std::cout << "Max queue occupancy: " << max_q_occupancy << "\n\n" << std::endl; - - std::vector> all_allocations; - - CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), - buffer->packets.size(), - GFXIP_MAJOR, - alloc_callback, - (void*) &all_allocations)); - - assert(all_allocations.size() == NUM_ACTIONS && - "QueueHammer test: Incorrect number of callbacks"); - for(auto& all_allocation : all_allocations) - { - pcsample_v1_t* samples = all_allocation.first; - size_t num_samples = all_allocation.second; - - assert(num_samples == NUM_QUEUES && "QueueHammer: Incorrect number of samples"); - assert(check_samples(samples, num_samples) && - "QueueHammer: parsed ID does not match correct ID"); - delete[] samples; - (void) num_samples; - } -} - -TEST(pcs_parser_correlation_id, multi_buffer) -{ - std::shared_ptr firstBuffer = std::make_shared(); - std::shared_ptr queue = std::make_shared(16, firstBuffer); - std::shared_ptr dispatch1 = std::make_shared(queue); - std::shared_ptr dispatch2 = std::make_shared(queue); - - firstBuffer->genUpcomingSamples(4); - MockWave(dispatch1).genPCSample(); - MockWave(dispatch2).genPCSample(); - MockWave(dispatch1).genPCSample(); - MockWave(dispatch2).genPCSample(); - - std::shared_ptr secondBuffer = std::make_shared(); - const auto& packets = firstBuffer->packets; - secondBuffer->packets = std::vector(packets.begin() + 2, packets.end()); - - std::vector> all_allocations; - - CHECK_PARSER(parse_buffer((generic_sample_t*) firstBuffer->packets.data(), - firstBuffer->packets.size(), - GFXIP_MAJOR, - alloc_callback, - (void*) &all_allocations)); - CHECK_PARSER(parse_buffer((generic_sample_t*) secondBuffer->packets.data(), - secondBuffer->packets.size(), - GFXIP_MAJOR, - alloc_callback, - (void*) &all_allocations)); - - assert(all_allocations.size() == 2 && "MultiBuffer: Incorrect number of callbacks"); - auto& sample = all_allocations[1]; - assert(sample.second == 4 && "MultiBuffer: Incorrect number of samples"); - assert(check_samples(sample.first, sample.second) && - "MultiBuffer: parsed ID does not match correct ID"); - - delete[] all_allocations[0].first; - delete[] all_allocations[1].first; - (void) sample; -}; - -/** - * Benchmarks how fast the parser can process samples on a single threaded case - * Current: 5600X with -Ofast, up to >140 million samples/s or ~9GB/s R/W (18GB/s bidirectional) - */ -static void -Benchmark(bool bWarmup) -{ - constexpr size_t SAMPLE_PER_DISPATCH = 8192; - constexpr size_t DISP_PER_QUEUE = 12; - constexpr size_t NUM_QUEUES = MockDoorBell::num_unique_bells; - - std::shared_ptr buffer = std::make_shared(); - std::array>, NUM_QUEUES> active_dispatches; - - for(size_t q = 0; q < NUM_QUEUES; q++) - { - std::shared_ptr queue = std::make_shared(DISP_PER_QUEUE * 2, buffer); - for(size_t d = 0; d < DISP_PER_QUEUE; d++) - active_dispatches[q].push_back(std::make_shared(queue)); - } - - constexpr size_t TOTAL_NUM_SAMPLES = NUM_QUEUES * DISP_PER_QUEUE * SAMPLE_PER_DISPATCH; - buffer->genUpcomingSamples(TOTAL_NUM_SAMPLES); - - for(auto& queue : active_dispatches) - for(auto& dispatch : queue) - for(size_t i = 0; i < SAMPLE_PER_DISPATCH; i++) - MockWave(dispatch).genPCSample(); - - std::pair userdata; - userdata.first = new pcsample_v1_t[TOTAL_NUM_SAMPLES]; - userdata.second = TOTAL_NUM_SAMPLES; - - auto t0 = std::chrono::system_clock::now(); - CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(), - buffer->packets.size(), - GFXIP_MAJOR, - [](pcsample_v1_t** sample, uint64_t size, void* userdata_) { - auto* pair = reinterpret_cast*>( - userdata_); - assert(TOTAL_NUM_SAMPLES == pair->second); - *sample = pair->first; - return size; - }, - &userdata)); - auto t1 = std::chrono::system_clock::now(); - float samples_per_us = float(TOTAL_NUM_SAMPLES) / (t1 - t0).count() * 1E3f; - - if(!bWarmup) - { - std::cout << "Benchmark: Parsed " << int(samples_per_us * 1E3f + 0.5f) * 1E-3f - << " Msample/s ("; - std::cout << int(sizeof(pcsample_v1_t) * samples_per_us) << " MB/s)" << std::endl; - } - - delete[] userdata.first; -} - -TEST(pcs_parser, benchmark) -{ - Benchmark(true); - Benchmark(false); - Benchmark(false); - Benchmark(false); -} - -class WaveSnapTest -{ -public: - WaveSnapTest() - { - buffer = std::make_shared(); - queue = std::make_shared(16, buffer); - dispatch = std::make_shared(queue); - } - - void Test() - { - FillBuffers(); - CheckBuffers(); - } - - virtual void FillBuffers() = 0; - virtual void CheckBuffers() = 0; - - void genPCSample(int wave_cnt, int inst_type, int reason, int arb_issue, int arb_stall) - { - wave_cnt &= 0x3F; - inst_type &= 0xF; - reason &= 0x7; - arb_issue &= 0xFF; - arb_stall &= 0xFF; - - perf_sample_snapshot_v1 snap; - ::memset(&snap, 0, sizeof(snap)); - snap.pc = dispatch->unique_id; - snap.correlation_id = dispatch->getMockId(); - - snap.perf_snapshot_data = (inst_type << 3) | (reason << 7); - snap.perf_snapshot_data |= (arb_issue << 10) | (arb_stall << 18); - snap.perf_snapshot_data1 = wave_cnt; - - assert(dispatch.get()); - dispatch->submit(packet_union_t{.snap = snap}); - }; - - std::shared_ptr buffer; - std::shared_ptr queue; - std::shared_ptr dispatch; -}; - -class WaveCntTest : public WaveSnapTest -{ -public: - void FillBuffers() override - { - // Loop over all possible wave_cnt - buffer->genUpcomingSamples(max_wave_number); - for(size_t i = 0; i < max_wave_number; i++) - genPCSample(i, GFX9::TYPE_LDS, GFX9::REASON_ALU, GFX9::ISSUE_VALU, GFX9::ISSUE_VALU); - } - - void CheckBuffers() override - { - auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 - assert(parsed.size() == 1); - assert(parsed[0].size() == max_wave_number); - - for(size_t i = 0; i < max_wave_number; i++) - assert(parsed[0][i].wave_count == i); - } - - const size_t max_wave_number = 64; - std::vector snapshots; -}; - -class InstTypeTest : public WaveSnapTest -{ -public: - void FillBuffers() override - { - // Loop over inst_type_issued - UNROLL_TYPECHECK(); - buffer->genUpcomingSamples(GFX9::TYPE_LAST); - for(int i = 0; i < GFX9::TYPE_LAST; i++) - genPCSample(i, i, GFX9::REASON_ALU, GFX9::ISSUE_MATRIX, GFX9::ISSUE_MATRIX); - } - - void CheckBuffers() override - { - auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 - assert(parsed.size() == 1); - assert(parsed[0].size() == GFX9::TYPE_LAST); - assert(snapshots.size() == GFX9::TYPE_LAST); - - for(size_t i = 0; i < GFX9::TYPE_LAST; i++) - assert(snapshots[i].inst_type == parsed[0][i].snapshot.inst_type); - } - - std::vector snapshots; -}; - -class StallReasonTest : public WaveSnapTest -{ -public: - void FillBuffers() override - { - // Loop over reason_not_issued - UNROLL_REASONCHECK(); - buffer->genUpcomingSamples(GFX9::REASON_LAST); - for(int i = 0; i < GFX9::REASON_LAST; i++) - genPCSample(i, GFX9::TYPE_MATRIX, i, GFX9::ISSUE_MATRIX, GFX9::ISSUE_MATRIX); - } - - void CheckBuffers() override - { - auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 - assert(parsed.size() == 1); - assert(parsed[0].size() == GFX9::REASON_LAST); - assert(snapshots.size() == GFX9::REASON_LAST); - - for(size_t i = 0; i < GFX9::REASON_LAST; i++) - assert(snapshots[i].reason_not_issued == parsed[0][i].snapshot.reason_not_issued); - } - - std::vector snapshots; -}; - -class ArbStateTest : public WaveSnapTest -{ -public: - void FillBuffers() override - { - // Loop over arb_state_issue - UNROLL_ARBCHECK(); - buffer->genUpcomingSamples(GFX9::ISSUE_LAST * GFX9::ISSUE_LAST); - for(int i = 0; i < GFX9::ISSUE_LAST; i++) - for(int j = 0; j < GFX9::ISSUE_LAST; j++) - genPCSample(i, GFX9::TYPE_MATRIX, GFX9::REASON_ALU, 1 << i, 1 << j); - } - - void CheckBuffers() override - { - auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 - assert(parsed.size() == 1); - assert(parsed[0].size() == GFX9::ISSUE_LAST * GFX9::ISSUE_LAST); - assert(snapshots.size() == GFX9::ISSUE_LAST * GFX9::ISSUE_LAST); - - for(size_t i = 0; i < GFX9::ISSUE_LAST * GFX9::ISSUE_LAST; i++) - { - auto& snap = snapshots[i]; - assert(snap.arb_state_issue == parsed[0][i].snapshot.arb_state_issue); - assert(snap.arb_state_stall == parsed[0][i].snapshot.arb_state_stall); - } - } - - std::vector snapshots; -}; - -class WaveIssueAndErrorTest : public WaveSnapTest -{ - void FillBuffers() override - { - buffer->genUpcomingSamples(16); - for(int valid = 0; valid <= 1; valid++) - for(int issued = 0; issued <= 1; issued++) - for(int dual = 0; dual <= 1; dual++) - for(int error = 0; error <= 1; error++) - genPCSample(valid, issued, dual, error); - } - - void CheckBuffers() override - { - const int num_combinations = 16; - auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 - assert(parsed.size() == 1); - assert(parsed[0].size() == num_combinations); - assert(compare.size() == num_combinations); - - for(size_t i = 0; i < num_combinations; i++) - { - assert(compare[i].flags.valid == parsed[0][i].flags.valid); - assert(compare[i].wave_issued == parsed[0][i].wave_issued); - assert(compare[i].snapshot.dual_issue_valu == parsed[0][i].snapshot.dual_issue_valu); - } - } - - union trap_snapshot_v1 - { - struct - { - uint32_t valid : 1; - uint32_t issued : 1; - uint32_t dual : 1; - uint32_t reserved : 23; - uint32_t error : 1; - uint32_t reserved2 : 5; - }; - uint32_t raw; - }; - - void genPCSample(bool valid, bool issued, bool dual, bool error) - { - pcsample_v1_t sample; - ::memset(&sample, 0, sizeof(sample)); - sample.pc = dispatch->unique_id; - sample.correlation_id = dispatch->getMockId(); - - sample.flags.valid = valid && !error; - sample.wave_issued = issued; - sample.snapshot.dual_issue_valu = dual; - - assert(dispatch.get()); - - compare.push_back(sample); - - trap_snapshot_v1 snap; - snap.valid = valid; - snap.issued = issued; - snap.dual = dual; - snap.error = error; - - perf_sample_snapshot_v1 pss; - pss.perf_snapshot_data = snap.raw; - pss.correlation_id = dispatch->getMockId(); - dispatch->submit(std::move(pss)); - }; - - std::vector compare; -}; - -class WaveOtherFieldsTest : public WaveSnapTest -{ - void FillBuffers() override - { - buffer->genUpcomingSamples(3); - genPCSample(1, 2, 3, 4, 5, 6, 7, 8); // Counting - genPCSample(3, 5, 7, 11, 13, 17, 19, 23); // Some prime numbers - genPCSample(23, 19, 17, 13, 11, 7, 5, 3); // Some reversed primes - } - - void CheckBuffers() override - { - auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9 - assert(parsed.size() == 1); - assert(parsed[0].size() == 3); - assert(compare.size() == 3); - - for(size_t i = 0; i < 3; i++) - { - assert(parsed[0][i].flags.has_stall_reason == true); - assert(parsed[0][i].flags.has_wave_cnt == true); - assert(parsed[0][i].flags.has_memory_counter == false); - - assert(compare[i].exec_mask == parsed[0][i].exec_mask); - assert(compare[i].workgroud_id_x == parsed[0][i].workgroud_id_x); - assert(compare[i].workgroud_id_y == parsed[0][i].workgroud_id_y); - assert(compare[i].workgroud_id_z == parsed[0][i].workgroud_id_z); - - assert(compare[i].chiplet == parsed[0][i].chiplet); - assert(compare[i].wave_id == parsed[0][i].wave_id); - assert(compare[i].hw_id == parsed[0][i].hw_id); - assert(compare[i].correlation_id == parsed[0][i].correlation_id); - } - } - - void genPCSample(int pc, int exec, int blkx, int blky, int blkz, int chip, int wave, int hwid) - { - pcsample_v1_t sample; - ::memset(&sample, 0, sizeof(sample)); - - sample.exec_mask = exec; - sample.workgroud_id_x = blkx; - sample.workgroud_id_y = blky; - sample.workgroud_id_z = blkz; - - sample.chiplet = chip; - sample.wave_id = wave; - sample.hw_id = hwid; - sample.correlation_id = dispatch->unique_id; - - compare.push_back(sample); - - perf_sample_snapshot_v1 snap; - ::memset(&snap, 0, sizeof(snap)); - snap.exec_mask = exec; - - snap.workgroud_id_x = blkx; - snap.workgroud_id_y = blky; - snap.workgroud_id_z = blkz; - snap.chiplet_and_wave_id = (chip << 8) | (wave & 0x3F); - snap.hw_id = hwid; - snap.correlation_id = dispatch->getMockId(); - - assert(dispatch.get()); - dispatch->submit(snap); - - (void) pc; - }; - - std::vector compare; -}; - -// FIXME (vladimir): For some reason, the test can stochastically fail. -// Did not have time to get into details. -TEST(pcs_parser, gfx9) -{ - WaveCntTest{}.Test(); - InstTypeTest{}.Test(); - StallReasonTest{}.Test(); - ArbStateTest{}.Test(); - WaveIssueAndErrorTest{}.Test(); - // FIXME: this might crash some time. - // WaveOtherFieldsTest{}.Test(); - - std::cout << "GFX9 Test Done." << std::endl; -} - -// TODO: refactor the tests, modularize them and extract unit tests -// from the integration f diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/translation.cpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/translation.cpp index e28c2f96f5..43ccc6307f 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/translation.cpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/translation.cpp @@ -39,9 +39,9 @@ PCSParserTranslation::copySampleHeader(const SType& sample) ret.pc = sample.pc; ret.exec_mask = sample.exec_mask; - ret.workgroud_id_x = sample.workgroud_id_x; - ret.workgroud_id_y = sample.workgroud_id_y; - ret.workgroud_id_z = sample.workgroud_id_z; + ret.workgroup_id_x = sample.workgroup_id_x; + ret.workgroup_id_y = sample.workgroup_id_y; + ret.workgroup_id_z = sample.workgroup_id_z; ret.chiplet = sample.chiplet_and_wave_id >> 8; ret.wave_id = sample.chiplet_and_wave_id & 0x3F; diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/translation.hpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/translation.hpp index 48101f63ee..133856ba49 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/translation.hpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/pc_sampling/parser/translation.hpp @@ -22,32 +22,120 @@ #pragma once +#include #include #include -#include "lib/rocprofiler-sdk/pc_sampling/parser/gfx11.hpp" -#include "lib/rocprofiler-sdk/pc_sampling/parser/gfx_unknown.hpp" -#include "lib/rocprofiler-sdk/pc_sampling/parser/gfx9.hpp" -#include "lib/rocprofiler-sdk/pc_sampling/parser/parser_types.hpp" -#include "lib/rocprofiler-sdk/pc_sampling/parser/rocr.hpp" +#include "gfx11.hpp" +#include "gfx_unknown.hpp" +#include "gfx9.hpp" +#include "parser_types.h" +#include "rocr.h" -pcsample_v1_t -copyHostTrapSample(const perf_sample_host_trap_v1& sample); - -class PCSParserTranslation +template +inline pcsample_v1_t +copySampleHeader(const SType& sample) { -public: - template - static pcsample_v1_t copySampleHeader(const SType& sample); + pcsample_v1_t ret; + ret.flags.raw = 0; + ret.flags.type = AMD_SNAPSHOT_V1; - template - static pcsample_v1_t copyStochasticSample(const perf_sample_snapshot_v1& sample); -}; + ret.pc = sample.pc; + ret.exec_mask = sample.exec_mask; + ret.workgroup_id_x = sample.workgroup_id_x; + ret.workgroup_id_y = sample.workgroup_id_y; + ret.workgroup_id_z = sample.workgroup_id_z; + + ret.chiplet = sample.chiplet_and_wave_id >> 8; + ret.wave_id = sample.chiplet_and_wave_id & 0x3F; + ret.hw_id = sample.hw_id; + ret.timestamp = sample.timestamp; + return ret; +} + +inline pcsample_v1_t +copyHostTrapSample(const perf_sample_host_trap_v1& sample) +{ + pcsample_v1_t ret = copySampleHeader(sample); + ret.flags.type = AMD_HOST_TRAP_V1; + return ret; +} + +template +inline pcsample_v1_t +copyStochasticSample(const perf_sample_snapshot_v1& sample); + +template <> +inline pcsample_v1_t +copyStochasticSample(const perf_sample_snapshot_v1& sample) +{ + pcsample_v1_t ret = copySampleHeader(sample); + ret.flags.valid = sample.perf_snapshot_data & (~sample.perf_snapshot_data >> 26) & 0x1; + // Check wave_id matches snapshot_wave_id + + ret.flags.has_wave_cnt = true; + ret.flags.has_stall_reason = true; + + ret.wave_count = sample.perf_snapshot_data1 & 0x3F; + + ret.wave_issued = sample.perf_snapshot_data >> 1; + ret.snapshot.dual_issue_valu = sample.perf_snapshot_data >> 2; + ret.snapshot.inst_type = sample.perf_snapshot_data >> 3; + ret.snapshot.reason_not_issued = (sample.perf_snapshot_data >> 7) & 0x7; + ret.snapshot.arb_state_issue = (sample.perf_snapshot_data >> 10) & 0xFF; + ret.snapshot.arb_state_stall = (sample.perf_snapshot_data >> 18) & 0xFF; + ret.memory_counters.raw = 0; + return ret; +} + +template <> +inline pcsample_v1_t +copyStochasticSample(const perf_sample_snapshot_v1& sample) +{ + pcsample_v1_t ret = copySampleHeader(sample); + ret.flags.valid = sample.perf_snapshot_data & (~sample.perf_snapshot_data >> 23) & 0x1; + // Check wave_id matches snapshot_wave_id + + ret.flags.has_stall_reason = true; + + ret.wave_issued = sample.perf_snapshot_data >> 1; + ret.snapshot.inst_type = sample.perf_snapshot_data >> 2; + ret.snapshot.reason_not_issued = (sample.perf_snapshot_data >> 6) & 0x7; + ret.snapshot.arb_state_issue = (sample.perf_snapshot_data >> 9) & 0x7F; + ret.snapshot.arb_state_stall = (sample.perf_snapshot_data >> 16) & 0x7F; + ret.snapshot.dual_issue_valu = false; + ret.memory_counters.raw = 0; + return ret; +} + +template <> +inline pcsample_v1_t +copyStochasticSample(const perf_sample_snapshot_v1& sample) +{ + pcsample_v1_t ret = copySampleHeader(sample); + ret.flags.valid = sample.perf_snapshot_data & 0x1; + // Check wave_id matches snapshot_wave_id + + ret.flags.has_wave_cnt = true; + ret.flags.has_stall_reason = true; + + ret.wave_issued = sample.perf_snapshot_data >> 1; + ret.snapshot.inst_type = sample.perf_snapshot_data >> 2; + ret.snapshot.reason_not_issued = (sample.perf_snapshot_data >> 6) & 0x7; + + ret.wave_count = sample.perf_snapshot_data1 & 0x3F; + ret.snapshot.arb_state_issue = (sample.perf_snapshot_data1 >> 6) & 0xFF; + ret.snapshot.arb_state_stall = (sample.perf_snapshot_data1 >> 14) & 0xFF; + + ret.flags.has_memory_counter = true; + ret.memory_counters.raw = sample.perf_snapshot_data2; + return ret; +} #define BITSHIFT(sname) out |= ((in >> GFX::sname) & 1) << PCSAMPLE::sname template -int +inline int translate_arb(int in) { size_t out = 0; @@ -114,19 +202,19 @@ public: }; template -int +inline int translate_reason(int in) { static GFX_REASON_LUT lut; - return lut[in & 0xF]; + return lut[in & 0x1F]; } template -int +inline int translate_inst(int in) { static GFX_INST_LUT lut; - return lut[in & 0xF]; + return lut[in & 0x1F]; } #undef LUTOVERLOAD @@ -137,8 +225,7 @@ copySample(const void* sample) { if(HostTrap) return copyHostTrapSample(*(const perf_sample_host_trap_v1*) sample); - pcsample_v1_t ret = - PCSParserTranslation::copyStochasticSample(*(const perf_sample_snapshot_v1*) sample); + pcsample_v1_t ret = copyStochasticSample(*(const perf_sample_snapshot_v1*) sample); ret.snapshot.inst_type = translate_inst(ret.snapshot.inst_type); ret.snapshot.arb_state_issue = translate_arb(ret.snapshot.arb_state_issue);