Host trap PC sampling uses new record type (#1207)

* Host trap PC sampling uses new record type

* removing redundant field

* formatting

* simplifying templates in the parser - no need for HostTrap boolean

* reviving some parser tests

* hw_id decoding on GFX9

* HW id parser test

* parser CID test

* Parser multigpu test

* removing rocprofiler_pc_sampling_record_t and some fields from hw_id

* simplifying parser context

* keep bench test internally

* initializing gfx9_hw_id_t differently

* anonymous struct first

* avoiding inlining initialization of struct
This commit is contained in:
Vladimir Indic
2024-11-20 21:02:47 +01:00
کامیت شده توسط GitHub
والد 55aea3ef10
کامیت bc52c17e64
21فایلهای تغییر یافته به همراه1319 افزوده شده و 738 حذف شده
@@ -31,6 +31,7 @@
* 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)
*/
template <typename PcSamplingRecordT>
static bool
Benchmark(bool bWarmup)
{
@@ -38,14 +39,16 @@ Benchmark(bool bWarmup)
constexpr size_t DISP_PER_QUEUE = 8;
constexpr size_t NUM_QUEUES = 4;
std::shared_ptr<MockRuntimeBuffer> buffer = std::make_shared<MockRuntimeBuffer>();
std::array<std::vector<std::shared_ptr<MockDispatch>>, NUM_QUEUES> active_dispatches;
auto buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>();
std::array<std::vector<std::shared_ptr<MockDispatch<PcSamplingRecordT>>>, NUM_QUEUES>
active_dispatches;
for(size_t q = 0; q < NUM_QUEUES; q++)
{
std::shared_ptr<MockQueue> queue = std::make_shared<MockQueue>(DISP_PER_QUEUE * 2, buffer);
auto queue = std::make_shared<MockQueue<PcSamplingRecordT>>(DISP_PER_QUEUE * 2, buffer);
for(size_t d = 0; d < DISP_PER_QUEUE; d++)
active_dispatches[q].push_back(std::make_shared<MockDispatch>(queue));
active_dispatches[q].push_back(
std::make_shared<MockDispatch<PcSamplingRecordT>>(queue));
}
constexpr size_t TOTAL_NUM_SAMPLES = NUM_QUEUES * DISP_PER_QUEUE * SAMPLE_PER_DISPATCH;
@@ -56,23 +59,24 @@ Benchmark(bool bWarmup)
for(size_t i = 0; i < SAMPLE_PER_DISPATCH; i++)
MockWave(dispatch).genPCSample();
std::pair<rocprofiler_pc_sampling_record_t*, size_t> userdata;
userdata.first = new rocprofiler_pc_sampling_record_t[TOTAL_NUM_SAMPLES];
std::pair<PcSamplingRecordT*, size_t> userdata;
userdata.first = new PcSamplingRecordT[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,
[](rocprofiler_pc_sampling_record_t** sample, uint64_t size, void* userdata_) {
auto* pair =
reinterpret_cast<std::pair<rocprofiler_pc_sampling_record_t*, size_t>*>(userdata_);
user_callback_t<PcSamplingRecordT> user_cb =
[](PcSamplingRecordT** sample, uint64_t size, void* userdata_) {
auto* pair = reinterpret_cast<std::pair<PcSamplingRecordT*, size_t>*>(userdata_);
assert(TOTAL_NUM_SAMPLES == pair->second);
*sample = pair->first;
return size;
},
&userdata));
};
auto t0 = std::chrono::system_clock::now();
CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
GFXIP_MAJOR,
user_cb,
&userdata));
auto t1 = std::chrono::system_clock::now();
float samples_per_us = float(TOTAL_NUM_SAMPLES) / (t1 - t0).count() * 1E3f;
@@ -80,8 +84,7 @@ Benchmark(bool bWarmup)
{
std::cout << "Benchmark: Parsed " << int(samples_per_us * 1E3f + 0.5f) * 1E-3f
<< " Msample/s (";
std::cout << int(sizeof(rocprofiler_pc_sampling_record_t) * samples_per_us) << " MB/s)"
<< std::endl;
std::cout << int(sizeof(PcSamplingRecordT) * samples_per_us) << " MB/s)" << std::endl;
}
delete[] userdata.first;
@@ -90,7 +93,12 @@ Benchmark(bool bWarmup)
TEST(pcs_parser, benchmark_test)
{
EXPECT_EQ(Benchmark(true), true);
EXPECT_EQ(Benchmark(false), true);
EXPECT_EQ(Benchmark(false), true);
// Tests for host trap v0 records
EXPECT_EQ(Benchmark<rocprofiler_pc_sampling_record_host_trap_v0_t>(true), true);
EXPECT_EQ(Benchmark<rocprofiler_pc_sampling_record_host_trap_v0_t>(false), true);
EXPECT_EQ(Benchmark<rocprofiler_pc_sampling_record_host_trap_v0_t>(false), true);
// tests for stochastic v0 records
EXPECT_EQ(Benchmark<rocprofiler_pc_sampling_record_stochastic_v0_t>(true), true);
EXPECT_EQ(Benchmark<rocprofiler_pc_sampling_record_stochastic_v0_t>(false), true);
EXPECT_EQ(Benchmark<rocprofiler_pc_sampling_record_stochastic_v0_t>(false), true);
}
@@ -33,15 +33,15 @@ std::mt19937 rdgen(1);
/**
* Sample user memory allocation callback.
* It expects userdata to be cast-able to a pointer to
* std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>>
* std::vector<std::pair<PcSamplingRecordT*, uint64_t>>
*/
template <typename PcSamplingRecordT>
static uint64_t
alloc_callback(rocprofiler_pc_sampling_record_t** buffer, uint64_t size, void* userdata)
alloc_callback(PcSamplingRecordT** buffer, uint64_t size, void* userdata)
{
*buffer = new rocprofiler_pc_sampling_record_t[size];
*buffer = new PcSamplingRecordT[size];
auto& vector =
*reinterpret_cast<std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>>*>(
userdata);
*reinterpret_cast<std::vector<std::pair<PcSamplingRecordT*, uint64_t>>*>(userdata);
vector.push_back({*buffer, size});
return size;
}
@@ -50,35 +50,34 @@ alloc_callback(rocprofiler_pc_sampling_record_t** buffer, uint64_t size, void* u
* Uses the MockWave dispatch's unique_id store in the pc field to verify
* the reconstructed correlation_id.
*/
template <typename PcSamplingRecordT>
static bool
check_samples(rocprofiler_pc_sampling_record_t* samples, uint64_t size)
check_samples(PcSamplingRecordT* samples, uint64_t size)
{
// TODO: replace with (code_obj_id, pc)
for(size_t i = 0; i < size; i++)
if(samples[i].correlation_id.internal != samples[i].pc.loaded_code_object_offset)
return false;
if(samples[i].correlation_id.internal != samples[i].pc.code_object_offset) return false;
return true;
}
/**
* Simplest mock classes use, generates a single queue+dispatch with 2 PC samples.
*/
TEST(pcs_parser, hello_world)
template <typename PcSamplingRecordT>
void
pcs_parser_hello_world()
{
std::shared_ptr<MockRuntimeBuffer> buffer = std::make_shared<MockRuntimeBuffer>();
std::shared_ptr<MockQueue> queue = std::make_shared<MockQueue>(16, buffer);
std::shared_ptr<MockDispatch> dispatch = std::make_shared<MockDispatch>(queue);
auto buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>();
auto queue = std::make_shared<MockQueue<PcSamplingRecordT>>(16, buffer);
auto dispatch = std::make_shared<MockDispatch<PcSamplingRecordT>>(queue);
buffer->genUpcomingSamples(2);
MockWave(dispatch).genPCSample();
MockWave(dispatch).genPCSample();
std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>> all_allocations;
std::vector<std::pair<PcSamplingRecordT*, uint64_t>> all_allocations;
CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
GFXIP_MAJOR,
alloc_callback,
alloc_callback<PcSamplingRecordT>,
(void*) &all_allocations));
EXPECT_EQ(all_allocations.size(), 1); // HelloWorld: Incorrect number of callbacks
@@ -91,23 +90,34 @@ TEST(pcs_parser, hello_world)
}
}
/**
* Simplest mock classes use, generates a single queue+dispatch with 2 PC samples.
*/
TEST(pcs_parser, hello_world)
{
pcs_parser_hello_world<rocprofiler_pc_sampling_record_host_trap_v0_t>();
pcs_parser_hello_world<rocprofiler_pc_sampling_record_stochastic_v0_t>();
}
/**
* 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)
template <typename PcSamplingRecordT>
void
pcs_parser_reverse_wave_order()
{
std::shared_ptr<MockRuntimeBuffer> buffer = std::make_shared<MockRuntimeBuffer>();
std::shared_ptr<MockQueue> queue1 = std::make_shared<MockQueue>(16, buffer);
std::shared_ptr<MockQueue> queue2 = std::make_shared<MockQueue>(16, buffer);
auto buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>();
auto queue1 = std::make_shared<MockQueue<PcSamplingRecordT>>(16, buffer);
auto queue2 = std::make_shared<MockQueue<PcSamplingRecordT>>(16, buffer);
std::vector<std::shared_ptr<MockDispatch>> dispatches;
dispatches.push_back(std::make_shared<MockDispatch>(queue1));
dispatches.push_back(std::make_shared<MockDispatch>(queue1));
dispatches.push_back(std::make_shared<MockDispatch>(queue2));
dispatches.push_back(std::make_shared<MockDispatch>(queue2));
dispatches.push_back(std::make_shared<MockDispatch>(queue1));
std::vector<std::shared_ptr<MockDispatch<PcSamplingRecordT>>> dispatches;
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue1));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue1));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue2));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue2));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue1));
buffer->genUpcomingSamples(dispatches.size());
for(auto it = dispatches.rbegin(); it != dispatches.rend(); it++)
@@ -116,12 +126,12 @@ TEST(pcs_parser, reverse_wave_order)
for(auto it = dispatches.begin(); it != dispatches.end(); it++)
MockWave(*it).genPCSample();
std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>> all_allocations;
std::vector<std::pair<PcSamplingRecordT*, uint64_t>> all_allocations;
CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
GFXIP_MAJOR,
alloc_callback,
alloc_callback<PcSamplingRecordT>,
(void*) &all_allocations));
EXPECT_EQ(all_allocations.size(), 2); // ReverseWaveOrder test: Incorrect number of callbacks
@@ -135,29 +145,33 @@ TEST(pcs_parser, reverse_wave_order)
}
}
/**
* 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)
TEST(pcs_parser, reverse_wave_order)
{
const int num_samples = 32;
std::shared_ptr<MockRuntimeBuffer> buffer = std::make_shared<MockRuntimeBuffer>();
std::shared_ptr<MockQueue> queue = std::make_shared<MockQueue>(5, buffer);
pcs_parser_reverse_wave_order<rocprofiler_pc_sampling_record_host_trap_v0_t>();
pcs_parser_reverse_wave_order<rocprofiler_pc_sampling_record_stochastic_v0_t>();
}
template <typename PcSamplingRecordT>
void
pcs_parser_dispatch_wrapping()
{
const int num_samples = 32;
auto buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>();
auto queue = std::make_shared<MockQueue<PcSamplingRecordT>>(5, buffer);
for(int i = 0; i < num_samples; i++)
{
auto dispatch = std::make_shared<MockDispatch>(queue);
auto dispatch = std::make_shared<MockDispatch<PcSamplingRecordT>>(queue);
buffer->genUpcomingSamples(1);
MockWave(dispatch).genPCSample();
}
std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>> all_allocations;
std::vector<std::pair<PcSamplingRecordT*, uint64_t>> all_allocations;
CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
GFXIP_MAJOR,
alloc_callback,
alloc_callback<PcSamplingRecordT>,
(void*) &all_allocations));
EXPECT_EQ(all_allocations.size(),
@@ -172,39 +186,47 @@ TEST(pcs_parser, dispatch_wrapping)
}
/**
* Creates a few queues with a few dispatchs per queue.
* Adds random samples per dispatch, and checks the result.
* 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, random_samples)
TEST(pcs_parser, dispatch_wrapping)
{
const int num_samples = 1024;
std::shared_ptr<MockRuntimeBuffer> buffer = std::make_shared<MockRuntimeBuffer>();
std::shared_ptr<MockQueue> queue1 = std::make_shared<MockQueue>(16, buffer);
std::shared_ptr<MockQueue> queue2 = std::make_shared<MockQueue>(16, buffer);
std::shared_ptr<MockQueue> queue3 = std::make_shared<MockQueue>(16, buffer);
std::shared_ptr<MockQueue> queue4 = std::make_shared<MockQueue>(16, buffer);
pcs_parser_dispatch_wrapping<rocprofiler_pc_sampling_record_host_trap_v0_t>();
pcs_parser_dispatch_wrapping<rocprofiler_pc_sampling_record_stochastic_v0_t>();
}
std::vector<std::shared_ptr<MockDispatch>> dispatches;
dispatches.push_back(std::make_shared<MockDispatch>(queue1));
dispatches.push_back(std::make_shared<MockDispatch>(queue1));
dispatches.push_back(std::make_shared<MockDispatch>(queue2));
dispatches.push_back(std::make_shared<MockDispatch>(queue3));
dispatches.push_back(std::make_shared<MockDispatch>(queue1));
dispatches.push_back(std::make_shared<MockDispatch>(queue3));
dispatches.push_back(std::make_shared<MockDispatch>(queue3));
dispatches.push_back(std::make_shared<MockDispatch>(queue2));
dispatches.push_back(std::make_shared<MockDispatch>(queue1));
template <typename PcSamplingRecordT>
void
pcs_parser_random_samples()
{
const int num_samples = 1024;
auto buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>();
auto queue1 = std::make_shared<MockQueue<PcSamplingRecordT>>(16, buffer);
auto queue2 = std::make_shared<MockQueue<PcSamplingRecordT>>(16, buffer);
auto queue3 = std::make_shared<MockQueue<PcSamplingRecordT>>(16, buffer);
auto queue4 = std::make_shared<MockQueue<PcSamplingRecordT>>(16, buffer);
std::vector<std::shared_ptr<MockDispatch<PcSamplingRecordT>>> dispatches;
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue1));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue1));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue2));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue3));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue1));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue3));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue3));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue2));
dispatches.push_back(std::make_shared<MockDispatch<PcSamplingRecordT>>(queue1));
buffer->genUpcomingSamples(num_samples);
for(int i = 0; i < num_samples; i++)
MockWave(dispatches[rdgen() % dispatches.size()]).genPCSample();
std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>> all_allocations;
std::vector<std::pair<PcSamplingRecordT*, uint64_t>> all_allocations;
CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
GFXIP_MAJOR,
alloc_callback,
alloc_callback<PcSamplingRecordT>,
(void*) &all_allocations));
EXPECT_EQ(all_allocations.size(), 1); // RandomSamples test: Incorrect number of callbacks
@@ -218,21 +240,29 @@ TEST(pcs_parser, random_samples)
}
/**
* 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.
* Creates a few queues with a few dispatchs per queue.
* Adds random samples per dispatch, and checks the result.
*/
TEST(pcs_parser, queue_hammer)
TEST(pcs_parser, random_samples)
{
pcs_parser_random_samples<rocprofiler_pc_sampling_record_host_trap_v0_t>();
pcs_parser_random_samples<rocprofiler_pc_sampling_record_stochastic_v0_t>();
}
template <typename PcSamplingRecordT>
void
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<MockRuntimeBuffer> buffer = std::make_shared<MockRuntimeBuffer>();
auto buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>();
std::array<std::shared_ptr<MockQueue>, NUM_QUEUES> queues;
std::array<std::vector<std::shared_ptr<MockDispatch>>, NUM_QUEUES> active_dispatches;
std::array<std::shared_ptr<MockQueue<PcSamplingRecordT>>, NUM_QUEUES> queues;
std::array<std::vector<std::shared_ptr<MockDispatch<PcSamplingRecordT>>>, NUM_QUEUES>
active_dispatches;
int num_reset_queues = 0;
int num_samples_generated = 0;
@@ -241,9 +271,10 @@ TEST(pcs_parser, queue_hammer)
size_t max_q_occupancy = 0;
for(int i = 0; i < NUM_QUEUES; i++)
queues[i] = std::make_shared<MockQueue>(QSIZE, buffer);
queues[i] = std::make_shared<MockQueue<PcSamplingRecordT>>(QSIZE, buffer);
for(int i = 0; i < NUM_QUEUES; i++)
active_dispatches[i].push_back(std::make_shared<MockDispatch>(queues[i]));
active_dispatches[i].push_back(
std::make_shared<MockDispatch<PcSamplingRecordT>>(queues[i]));
for(int i = 0; i < NUM_ACTIONS; i++)
{
@@ -254,7 +285,7 @@ TEST(pcs_parser, queue_hammer)
// Delete queue and create new one
active_dispatches[q] = {};
queues[q].reset();
queues[q] = std::make_shared<MockQueue>(QSIZE, buffer);
queues[q] = std::make_shared<MockQueue<PcSamplingRecordT>>(QSIZE, buffer);
num_reset_queues++;
}
else if(action > ACTION_MAX / 2 && active_dispatches[q].size() > 1)
@@ -267,7 +298,8 @@ TEST(pcs_parser, queue_hammer)
// Add new dispatch
if(active_dispatches[q].size() < QSIZE)
{
active_dispatches[q].push_back(std::make_shared<MockDispatch>(queues[q]));
active_dispatches[q].push_back(
std::make_shared<MockDispatch<PcSamplingRecordT>>(queues[q]));
num_dispatches_generated += 1;
}
@@ -276,7 +308,8 @@ TEST(pcs_parser, queue_hammer)
for(auto& queue : active_dispatches)
{
EXPECT_NE(queue.size(), 0);
std::shared_ptr<MockDispatch> rand_dispatch = queue[rdgen() % queue.size()];
std::shared_ptr<MockDispatch<PcSamplingRecordT>> rand_dispatch =
queue[rdgen() % queue.size()];
MockWave(rand_dispatch).genPCSample();
num_samples_generated += 1;
avg_q_occupancy += queue.size();
@@ -292,20 +325,20 @@ TEST(pcs_parser, queue_hammer)
<< std::endl;
std::cout << "Max queue occupancy: " << max_q_occupancy << "\n\n" << std::endl;
std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>> all_allocations;
std::vector<std::pair<PcSamplingRecordT*, uint64_t>> all_allocations;
CHECK_PARSER(parse_buffer((generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
GFXIP_MAJOR,
alloc_callback,
alloc_callback<PcSamplingRecordT>,
(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++)
{
rocprofiler_pc_sampling_record_t* samples = all_allocations[sb].first;
size_t num_samples = all_allocations[sb].second;
PcSamplingRecordT* 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),
@@ -314,12 +347,25 @@ TEST(pcs_parser, queue_hammer)
}
}
TEST(pcs_parser, multi_buffer)
/**
* 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)
{
std::shared_ptr<MockRuntimeBuffer> firstBuffer = std::make_shared<MockRuntimeBuffer>();
std::shared_ptr<MockQueue> queue = std::make_shared<MockQueue>(16, firstBuffer);
std::shared_ptr<MockDispatch> dispatch1 = std::make_shared<MockDispatch>(queue);
std::shared_ptr<MockDispatch> dispatch2 = std::make_shared<MockDispatch>(queue);
pcs_parser_queue_hammer<rocprofiler_pc_sampling_record_host_trap_v0_t>();
pcs_parser_queue_hammer<rocprofiler_pc_sampling_record_stochastic_v0_t>();
}
template <typename PcSamplingRecordT>
void
pcs_parser_multi_buffer()
{
auto firstBuffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>();
auto queue = std::make_shared<MockQueue<PcSamplingRecordT>>(16, firstBuffer);
auto dispatch1 = std::make_shared<MockDispatch<PcSamplingRecordT>>(queue);
auto dispatch2 = std::make_shared<MockDispatch<PcSamplingRecordT>>(queue);
firstBuffer->genUpcomingSamples(4);
MockWave(dispatch1).genPCSample();
@@ -327,21 +373,21 @@ TEST(pcs_parser, multi_buffer)
MockWave(dispatch1).genPCSample();
MockWave(dispatch2).genPCSample();
std::shared_ptr<MockRuntimeBuffer> secondBuffer = std::make_shared<MockRuntimeBuffer>();
const auto& packets = firstBuffer->packets;
secondBuffer->packets = std::vector<packet_union_t>(packets.begin() + 2, packets.end());
auto secondBuffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>();
const auto& packets = firstBuffer->packets;
secondBuffer->packets = std::vector<packet_union_t>(packets.begin() + 2, packets.end());
std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>> all_allocations;
std::vector<std::pair<PcSamplingRecordT*, uint64_t>> all_allocations;
CHECK_PARSER(parse_buffer((generic_sample_t*) firstBuffer->packets.data(),
firstBuffer->packets.size(),
GFXIP_MAJOR,
alloc_callback,
alloc_callback<PcSamplingRecordT>,
(void*) &all_allocations));
CHECK_PARSER(parse_buffer((generic_sample_t*) secondBuffer->packets.data(),
secondBuffer->packets.size(),
GFXIP_MAJOR,
alloc_callback,
alloc_callback<PcSamplingRecordT>,
(void*) &all_allocations));
EXPECT_EQ(all_allocations.size(), 2); // MultiBuffer: Incorrect number of callbacks
@@ -352,4 +398,10 @@ TEST(pcs_parser, multi_buffer)
delete[] all_allocations[0].first;
delete[] all_allocations[1].first;
};
}
TEST(pcs_parser, multi_buffer)
{
pcs_parser_multi_buffer<rocprofiler_pc_sampling_record_host_trap_v0_t>();
pcs_parser_multi_buffer<rocprofiler_pc_sampling_record_stochastic_v0_t>();
}
@@ -100,14 +100,15 @@
ARBCHECK2(ISSUE_EXP); \
ARBCHECK2(ISSUE_MISC);
template <typename PcSamplingRecordT>
class WaveSnapTest
{
public:
WaveSnapTest()
{
buffer = std::make_shared<MockRuntimeBuffer>();
queue = std::make_shared<MockQueue>(16, buffer);
dispatch = std::make_shared<MockDispatch>(queue);
buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>();
queue = std::make_shared<MockQueue<PcSamplingRecordT>>(16, buffer);
dispatch = std::make_shared<MockDispatch<PcSamplingRecordT>>(queue);
}
void Test()
@@ -140,25 +141,27 @@ public:
dispatch->submit(packet_union_t{.snap = snap});
};
std::shared_ptr<MockRuntimeBuffer> buffer;
std::shared_ptr<MockQueue> queue;
std::shared_ptr<MockDispatch> dispatch;
std::shared_ptr<MockRuntimeBuffer<PcSamplingRecordT>> buffer;
std::shared_ptr<MockQueue<PcSamplingRecordT>> queue;
std::shared_ptr<MockDispatch<PcSamplingRecordT>> dispatch;
};
class WaveCntTest : public WaveSnapTest
template <typename PcSamplingRecordT>
class WaveCntTest : public WaveSnapTest<PcSamplingRecordT>
{
public:
void FillBuffers() override
{
// Loop over all possible wave_cnt
buffer->genUpcomingSamples(max_wave_number);
this->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);
this->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
auto parsed = this->buffer->get_parsed_buffer(9); // GFXIP==9
assert(parsed.size() == 1);
assert(parsed[0].size() == max_wave_number);
@@ -166,204 +169,336 @@ public:
assert(parsed[0][i].wave_count == i);
}
const size_t max_wave_number = 64;
std::vector<rocprofiler_pc_sampling_snapshot_v1_t> snapshots;
const size_t max_wave_number = 64;
std::vector<PcSamplingRecordT> snapshots;
};
class InstTypeTest : public WaveSnapTest
// 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<rocprofiler_pc_sampling_snapshot_v1_t> 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<rocprofiler_pc_sampling_snapshot_v1_t> 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<rocprofiler_pc_sampling_snapshot_v1_t> 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)
// {
// rocprofiler_pc_sampling_record_t sample;
// ::memset(&sample, 0, sizeof(sample));
// // TODO: Since code objects are not mocked, use pc.code_object_offset
// // as the absolute physical address of the mocked PC.
// sample.pc.code_object_offset = dispatch->unique_id;
// sample.correlation_id.internal = dispatch->getMockId().raw;
// 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().raw;
// dispatch->submit(std::move(pss));
// };
// std::vector<rocprofiler_pc_sampling_record_t> compare;
// };
template <typename PcSamplingRecordT>
class HwIdTest : public WaveSnapTest<PcSamplingRecordT>
{
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<rocprofiler_pc_sampling_snapshot_v1_t> 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<rocprofiler_pc_sampling_snapshot_v1_t> 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<rocprofiler_pc_sampling_snapshot_v1_t> 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
union gfx9_hw_id_t
{
uint32_t raw;
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 wave_id : 4; ///< wave slot index
uint32_t simd_id : 2; ///< SIMD index
uint32_t pipe_id : 2; ///< pipe index
uint32_t cu_id : 4; ///< Index of compute unit on GFX9 or workgroup processer on other
///< architectures
uint32_t shader_array_id : 1; ///< Shared array index
uint32_t shader_engine_id : 3; ///< shared engine index
uint32_t
threadgroup_id : 4; ///< thread_group index on GFX9, and workgroup index on GFX10+
uint32_t vm_id : 4; ///< virtual memory ID
uint32_t queue_id : 3; ///< queue id
uint32_t gfx_context_state_id : 3; ///< GFX context (state) id (only on GFX9) - ignored
uint32_t microengine_id : 2; ///< ACE (microengine) index
};
uint32_t raw;
};
void genPCSample(bool valid, bool issued, bool dual, bool error)
{
rocprofiler_pc_sampling_record_t sample;
::memset(&sample, 0, sizeof(sample));
// TODO: Since code objects are not mocked, use pc.loaded_code_object_offset
// as the absolute physical address of the mocked PC.
sample.pc.loaded_code_object_offset = dispatch->unique_id;
sample.correlation_id.internal = dispatch->getMockId().raw;
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().raw;
dispatch->submit(std::move(pss));
};
std::vector<rocprofiler_pc_sampling_record_t> 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
gfx9_hw_id_t hw_id_val0;
hw_id_val0.wave_id = 0;
hw_id_val0.simd_id = 0;
hw_id_val0.pipe_id = 0;
hw_id_val0.cu_id = 0;
hw_id_val0.shader_array_id = 0;
hw_id_val0.shader_engine_id = 0;
hw_id_val0.threadgroup_id = 0;
hw_id_val0.vm_id = 0;
hw_id_val0.queue_id = 0;
hw_id_val0.gfx_context_state_id = 0;
hw_id_val0.microengine_id = 0;
gfx9_hw_id_t hw_id_val1;
hw_id_val0.wave_id = 15;
hw_id_val0.simd_id = 3;
hw_id_val0.pipe_id = 3;
hw_id_val0.cu_id = 15;
hw_id_val0.shader_array_id = 1;
hw_id_val0.shader_engine_id = 7;
hw_id_val0.threadgroup_id = 15;
hw_id_val0.vm_id = 15;
hw_id_val0.queue_id = 7;
hw_id_val0.gfx_context_state_id = 7;
hw_id_val0.microengine_id = 3;
gfx9_hw_id_t hw_id_val2;
hw_id_val2.wave_id = 7;
hw_id_val2.simd_id = 2;
hw_id_val2.pipe_id = 2;
hw_id_val2.cu_id = 6;
hw_id_val2.shader_array_id = 0;
hw_id_val2.shader_engine_id = 3;
hw_id_val2.threadgroup_id = 8;
hw_id_val2.vm_id = 9;
hw_id_val2.queue_id = 3;
hw_id_val2.gfx_context_state_id = 2;
hw_id_val2.microengine_id = 1;
this->buffer->genUpcomingSamples(3);
genPCSample(hw_id_val0);
genPCSample(hw_id_val1);
genPCSample(hw_id_val2);
}
void CheckBuffers() override
{
auto parsed = buffer->get_parsed_buffer(9); // GFXIP==9
auto parsed = this->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.reserved == false);
// Comparing individual fields
assert(compare[i].hw_id.wave_id == parsed[0][i].hw_id.wave_id);
assert(compare[i].hw_id.simd_id == parsed[0][i].hw_id.simd_id);
assert(compare[i].hw_id.pipe_id == parsed[0][i].hw_id.pipe_id);
assert(compare[i].hw_id.cu_or_wgp_id == parsed[0][i].hw_id.cu_or_wgp_id);
assert(compare[i].hw_id.shader_array_id == parsed[0][i].hw_id.shader_array_id);
assert(compare[i].hw_id.shader_engine_id == parsed[0][i].hw_id.shader_engine_id);
assert(compare[i].hw_id.workgroup_id == parsed[0][i].hw_id.workgroup_id);
assert(compare[i].hw_id.vm_id == parsed[0][i].hw_id.vm_id);
assert(compare[i].hw_id.queue_id == parsed[0][i].hw_id.queue_id);
assert(compare[i].hw_id.microengine_id == parsed[0][i].hw_id.microengine_id);
}
}
void genPCSample(gfx9_hw_id_t hw_id)
{
PcSamplingRecordT sample;
::memset(&sample, 0, sizeof(sample));
// Unpacking individual fields
// NOTE: chiplet is tested in a WaveOtherFieldsTest test, becuase it's not
// transferred via hw_id, but chiplet_and_wave_id field.
sample.hw_id.wave_id = hw_id.wave_id;
sample.hw_id.simd_id = hw_id.simd_id;
sample.hw_id.pipe_id = hw_id.pipe_id;
sample.hw_id.cu_or_wgp_id = hw_id.cu_id;
sample.hw_id.shader_array_id = hw_id.shader_array_id;
sample.hw_id.shader_engine_id = hw_id.shader_engine_id;
sample.hw_id.workgroup_id = hw_id.threadgroup_id;
sample.hw_id.vm_id = hw_id.vm_id;
sample.hw_id.queue_id = hw_id.queue_id;
sample.hw_id.microengine_id = hw_id.microengine_id;
compare.push_back(sample);
perf_sample_snapshot_v1 snap;
::memset(&snap, 0, sizeof(snap));
// raw register value
snap.hw_id = hw_id.raw;
snap.correlation_id = this->dispatch->getMockId().raw;
assert(this->dispatch.get());
this->dispatch->submit(snap);
};
std::vector<PcSamplingRecordT> compare;
};
template <typename PcSamplingRecordT>
class WaveOtherFieldsTest : public WaveSnapTest<PcSamplingRecordT>
{
void FillBuffers() override
{
this->buffer->genUpcomingSamples(3);
genPCSample(1, 2, 3, 4, 5, 6, 7); // Counting
genPCSample(3, 5, 7, 11, 13, 17, 19); // Some prime numbers
genPCSample(23, 19, 17, 13, 11, 7, 5); // Some reversed primes
}
void CheckBuffers() override
{
auto parsed = this->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++)
{
// TODO: if we decide to test flags, make specialization for
// rocprofiler_pc_sampling_record_stochastic_v0_t
// assert(parsed[0][i].flags.has_stall_reason == true);
// assert(parsed[0][i].flags.has_wave_cnt == true);
// assert(parsed[0][i].flags.reserved == false);
assert(compare[i].exec_mask == parsed[0][i].exec_mask);
assert(compare[i].workgroup_id == parsed[0][i].workgroup_id);
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].hw_id.chiplet == parsed[0][i].hw_id.chiplet);
assert(compare[i].wave_in_group == parsed[0][i].wave_in_group);
// TODO: handle HW_ID as well.
// assert(compare[i].hw_id == parsed[0][i].hw_id);
assert(compare[i].correlation_id.internal == parsed[0][i].correlation_id.internal);
}
}
void genPCSample(int pc, int exec, int blkx, int blky, int blkz, int chip, int wave, int hwid)
void genPCSample(int pc, int exec, int blkx, int blky, int blkz, int chip, int wave)
{
rocprofiler_pc_sampling_record_t sample;
PcSamplingRecordT sample;
::memset(&sample, 0, sizeof(sample));
sample.exec_mask = exec;
@@ -371,13 +506,15 @@ class WaveOtherFieldsTest : public WaveSnapTest
sample.workgroup_id.y = blky;
sample.workgroup_id.z = blkz;
sample.chiplet = chip;
sample.wave_id = wave;
sample.hw_id = hwid;
sample.correlation_id.internal = dispatch->unique_id;
sample.hw_id.chiplet = chip;
sample.wave_in_group = wave;
sample.correlation_id.internal = this->dispatch->unique_id;
compare.push_back(sample);
// We're testing fields commong for both perf_sample_host_trap_v1 and
// perf_sample_snapshot_v1, so either struct is suitable here. No need to make
// specialization,
perf_sample_snapshot_v1 snap;
::memset(&snap, 0, sizeof(snap));
snap.exec_mask = exec;
@@ -386,26 +523,31 @@ class WaveOtherFieldsTest : public WaveSnapTest
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().raw;
snap.correlation_id = this->dispatch->getMockId().raw;
assert(dispatch.get());
dispatch->submit(snap);
assert(this->dispatch.get());
this->dispatch->submit(snap);
(void) pc;
};
std::vector<rocprofiler_pc_sampling_record_t> compare;
std::vector<PcSamplingRecordT> compare;
};
TEST(pcs_parser, gfx9_test)
{
WaveCntTest{}.Test();
InstTypeTest{}.Test();
StallReasonTest{}.Test();
ArbStateTest{}.Test();
WaveIssueAndErrorTest{}.Test();
WaveOtherFieldsTest{}.Test();
// Tests specific to stochastic sampling only
WaveCntTest<rocprofiler_pc_sampling_record_stochastic_v0_t>{}.Test();
// InstTypeTest{}.Test();
// StallReasonTest{}.Test();
// ArbStateTest{}.Test();
// WaveIssueAndErrorTest{}.Test();
// Tests commong for both host trap and stochastic sampling.
HwIdTest<rocprofiler_pc_sampling_record_host_trap_v0_t>{}.Test();
HwIdTest<rocprofiler_pc_sampling_record_stochastic_v0_t>{}.Test();
WaveOtherFieldsTest<rocprofiler_pc_sampling_record_host_trap_v0_t>{}.Test();
WaveOtherFieldsTest<rocprofiler_pc_sampling_record_stochastic_v0_t>{}.Test();
std::cout << "GFX9 Test Done." << std::endl;
}
@@ -46,6 +46,7 @@
/**
* Mimics the rocprofiler buffer sent to the parser.
*/
template <typename PcSamplingRecordT>
class MockRuntimeBuffer
{
public:
@@ -59,18 +60,21 @@ public:
void submit(const packet_union_t& packet) { packets.push_back(packet); };
//! Submits a "upcoming_samples_t" packet signaling the next num_samples packets are PC samples
void genUpcomingSamples(int num_samples)
void genUpcomingSamples(int num_samples, upcoming_sample_t sample_type)
{
packet_union_t uni;
::memset(&uni, 0, sizeof(uni));
uni.upcoming.type = AMD_UPCOMING_SAMPLES;
uni.upcoming.which_sample_type = AMD_SNAPSHOT_V1;
uni.upcoming.which_sample_type = sample_type;
uni.upcoming.num_samples = num_samples;
uni.upcoming.device.handle = device;
submit(uni);
}
std::vector<std::vector<rocprofiler_pc_sampling_record_t>> get_parsed_buffer(int GFXIP_MAJOR)
//! Submits a "upcoming_samples_t" packet signaling the next num_samples packets are PC samples
void genUpcomingSamples(int num_samples);
std::vector<std::vector<PcSamplingRecordT>> get_parsed_buffer(int GFXIP_MAJOR)
{
parsed_data = {};
@@ -83,22 +87,38 @@ public:
return parsed_data;
}
static uint64_t alloc_parse_memory(rocprofiler_pc_sampling_record_t** sample,
uint64_t req_size,
void* userdata)
static uint64_t alloc_parse_memory(PcSamplingRecordT** sample,
uint64_t req_size,
void* userdata)
{
auto* buffer = reinterpret_cast<MockRuntimeBuffer*>(userdata);
buffer->parsed_data.push_back(std::vector<rocprofiler_pc_sampling_record_t>(req_size));
buffer->parsed_data.push_back(std::vector<PcSamplingRecordT>(req_size));
*sample = buffer->parsed_data.back().data();
return req_size;
}
std::vector<packet_union_t> packets;
std::vector<std::vector<rocprofiler_pc_sampling_record_t>> parsed_data;
std::vector<packet_union_t> packets;
std::vector<std::vector<PcSamplingRecordT>> parsed_data;
const uint32_t device;
};
template <>
void
MockRuntimeBuffer<rocprofiler_pc_sampling_record_host_trap_v0_t>::genUpcomingSamples(
int num_samples)
{
genUpcomingSamples(num_samples, AMD_HOST_TRAP_V1);
}
template <>
void
MockRuntimeBuffer<rocprofiler_pc_sampling_record_stochastic_v0_t>::genUpcomingSamples(
int num_samples)
{
this->genUpcomingSamples(num_samples, AMD_SNAPSHOT_V1);
}
/**
* Mimics a HSA doorbell. Every live instance of this class has an unique ID (handler).
* The handler itself may be not unique considering dead instances.
@@ -149,10 +169,11 @@ private:
* read and write pointers.
* Creating an instance of this class automatically adds a queue creation packet to the buffer.
*/
template <typename PcSamplingRecordT>
class MockQueue
{
public:
MockQueue(int size_, std::shared_ptr<MockRuntimeBuffer>& buffer_)
MockQueue(int size_, std::shared_ptr<MockRuntimeBuffer<PcSamplingRecordT>>& buffer_)
: id(getUniqueId())
, size(size_)
, doorbell()
@@ -184,7 +205,7 @@ public:
const MockDoorBell doorbell;
const uint32_t device;
std::shared_ptr<MockRuntimeBuffer> const buffer;
std::shared_ptr<MockRuntimeBuffer<PcSamplingRecordT>> const buffer;
private:
static size_t getUniqueId()
@@ -198,10 +219,11 @@ private:
* Mimics a kernel dispatch.
* Creating an instance of this class automatically adds a dispatch creation packet to the buffer.
*/
template <typename PcSamplingRecordT>
class MockDispatch
{
public:
MockDispatch(std::shared_ptr<MockQueue>& queue_)
MockDispatch(std::shared_ptr<MockQueue<PcSamplingRecordT>>& queue_)
: queue(queue_)
, dispatch_id(queue->write_index)
, doorbell_id(queue->doorbell.handler)
@@ -251,7 +273,7 @@ public:
<< " ds_id:" << dispatch_id << std::endl;
}
std::shared_ptr<MockQueue> const queue;
std::shared_ptr<MockQueue<PcSamplingRecordT>> const queue;
const size_t dispatch_id;
const size_t doorbell_id;
@@ -273,10 +295,11 @@ private:
* Instead of generating a valid program counter, this class uses the snapshot.pc field to
* store the original dispatch's unique_id for later correctness verification.
*/
template <typename PcSamplingRecordT>
class MockWave
{
public:
MockWave(const std::shared_ptr<MockDispatch>& dispatch_)
MockWave(const std::shared_ptr<MockDispatch<PcSamplingRecordT>>& dispatch_)
: dispatch(dispatch_)
{}
@@ -295,5 +318,5 @@ public:
<< dispatch->unique_id << std::endl;
}
std::shared_ptr<MockDispatch> const dispatch;
std::shared_ptr<MockDispatch<PcSamplingRecordT>> const dispatch;
};
@@ -46,19 +46,20 @@ public:
/**
* Sample user memory allocation callback.
* It expects userdata to be cast-able to a pointer to
* std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>>
* std::vector<std::pair<PcSamplingRecordT*, uint64_t>>
*/
template <typename PcSamplingRecordT>
static uint64_t
alloc_callback(rocprofiler_pc_sampling_record_t** buffer, uint64_t size, void* userdata)
alloc_callback(PcSamplingRecordT** buffer, uint64_t size, void* userdata)
{
*buffer = new rocprofiler_pc_sampling_record_t[size];
*buffer = new PcSamplingRecordT[size];
auto& vector =
*reinterpret_cast<std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>>*>(
userdata);
*reinterpret_cast<std::vector<std::pair<PcSamplingRecordT*, uint64_t>>*>(userdata);
vector.push_back({*buffer, size});
return size;
}
template <typename PcSamplingRecordT>
void
multithread_queue_hammer(size_t tid, Latch* latch)
{
@@ -70,10 +71,11 @@ multithread_queue_hammer(size_t tid, Latch* latch)
constexpr int NUM_QUEUES = MockDoorBell::num_unique_bells / NUM_THREADS;
constexpr int ACTION_MAX = QSIZE * NUM_QUEUES / 2;
std::shared_ptr<MockRuntimeBuffer> buffer = std::make_shared<MockRuntimeBuffer>(tid);
auto buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>(tid);
std::array<std::shared_ptr<MockQueue>, NUM_QUEUES> queues;
std::array<std::vector<std::shared_ptr<MockDispatch>>, NUM_QUEUES> active_dispatches;
std::array<std::shared_ptr<MockQueue<PcSamplingRecordT>>, NUM_QUEUES> queues;
std::array<std::vector<std::shared_ptr<MockDispatch<PcSamplingRecordT>>>, NUM_QUEUES>
active_dispatches;
int num_reset_queues = 0;
int num_samples_generated = 0;
@@ -82,9 +84,10 @@ multithread_queue_hammer(size_t tid, Latch* latch)
size_t max_q_occupancy = 0;
for(int i = 0; i < NUM_QUEUES; i++)
queues[i] = std::make_shared<MockQueue>(QSIZE, buffer);
queues[i] = std::make_shared<MockQueue<PcSamplingRecordT>>(QSIZE, buffer);
for(int i = 0; i < NUM_QUEUES; i++)
active_dispatches[i].push_back(std::make_shared<MockDispatch>(queues[i]));
active_dispatches[i].push_back(
std::make_shared<MockDispatch<PcSamplingRecordT>>(queues[i]));
for(int i = 0; i < NUM_ACTIONS; i++)
{
@@ -95,7 +98,7 @@ multithread_queue_hammer(size_t tid, Latch* latch)
// Delete queue and create new one
active_dispatches[q] = {};
queues[q].reset();
queues[q] = std::make_shared<MockQueue>(QSIZE, buffer);
queues[q] = std::make_shared<MockQueue<PcSamplingRecordT>>(QSIZE, buffer);
num_reset_queues++;
}
else if(action > ACTION_MAX / 2 && active_dispatches[q].size() > 1)
@@ -108,7 +111,8 @@ multithread_queue_hammer(size_t tid, Latch* latch)
// Add new dispatch
if(active_dispatches[q].size() < QSIZE)
{
active_dispatches[q].push_back(std::make_shared<MockDispatch>(queues[q]));
active_dispatches[q].push_back(
std::make_shared<MockDispatch<PcSamplingRecordT>>(queues[q]));
num_dispatches_generated += 1;
}
@@ -117,7 +121,8 @@ multithread_queue_hammer(size_t tid, Latch* latch)
for(auto& queue : active_dispatches)
{
EXPECT_NE(queue.size(), 0);
std::shared_ptr<MockDispatch> rand_dispatch = queue[rdgen() % queue.size()];
std::shared_ptr<MockDispatch<PcSamplingRecordT>> rand_dispatch =
queue[rdgen() % queue.size()];
MockWave(rand_dispatch).genPCSample();
num_samples_generated += 1;
avg_q_occupancy += queue.size();
@@ -127,23 +132,23 @@ multithread_queue_hammer(size_t tid, Latch* latch)
latch->sync();
std::vector<std::pair<rocprofiler_pc_sampling_record_t*, uint64_t>> all_allocations;
std::vector<std::pair<PcSamplingRecordT*, uint64_t>> all_allocations;
CHECK_PARSER(_parse_buffer<GFX9>((generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
alloc_callback,
alloc_callback<PcSamplingRecordT>,
(void*) &all_allocations,
&corr_map));
EXPECT_EQ(all_allocations.size(), NUM_ACTIONS); // Incorrect number of callbacks
for(auto sb = 0ul; sb < all_allocations.size(); sb++)
{
rocprofiler_pc_sampling_record_t* samples = all_allocations[sb].first;
size_t num_samples = all_allocations[sb].second;
PcSamplingRecordT* samples = all_allocations[sb].first;
size_t num_samples = all_allocations[sb].second;
EXPECT_EQ(num_samples, NUM_QUEUES);
for(size_t i = 0; i < num_samples; i++)
EXPECT_EQ(samples[i].correlation_id.internal, samples[i].pc.loaded_code_object_offset);
EXPECT_EQ(samples[i].correlation_id.internal, samples[i].pc.code_object_offset);
delete[] samples;
}
}
@@ -152,6 +157,7 @@ multithread_queue_hammer(size_t tid, Latch* latch)
* 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)
*/
template <typename PcSamplingRecordT>
static std::pair<size_t, size_t>
MultiThread_BenchMark(size_t tid, Latch* latch)
{
@@ -161,14 +167,16 @@ MultiThread_BenchMark(size_t tid, Latch* latch)
constexpr size_t DISP_PER_QUEUE = 16;
constexpr size_t NUM_QUEUES = 1;
std::shared_ptr<MockRuntimeBuffer> buffer = std::make_shared<MockRuntimeBuffer>(tid);
std::array<std::vector<std::shared_ptr<MockDispatch>>, NUM_QUEUES> active_dispatches;
auto buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>(tid);
std::array<std::vector<std::shared_ptr<MockDispatch<PcSamplingRecordT>>>, NUM_QUEUES>
active_dispatches;
for(size_t q = 0; q < NUM_QUEUES; q++)
{
std::shared_ptr<MockQueue> queue = std::make_shared<MockQueue>(DISP_PER_QUEUE * 2, buffer);
auto queue = std::make_shared<MockQueue<PcSamplingRecordT>>(DISP_PER_QUEUE * 2, buffer);
for(size_t d = 0; d < DISP_PER_QUEUE; d++)
active_dispatches[q].push_back(std::make_shared<MockDispatch>(queue));
active_dispatches[q].push_back(
std::make_shared<MockDispatch<PcSamplingRecordT>>(queue));
}
constexpr size_t TOTAL_NUM_SAMPLES = NUM_QUEUES * DISP_PER_QUEUE * SAMPLE_PER_DISPATCH;
@@ -179,29 +187,31 @@ MultiThread_BenchMark(size_t tid, Latch* latch)
for(size_t i = 0; i < SAMPLE_PER_DISPATCH; i++)
MockWave(dispatch).genPCSample();
std::pair<rocprofiler_pc_sampling_record_t*, size_t> userdata;
userdata.first = new rocprofiler_pc_sampling_record_t[TOTAL_NUM_SAMPLES];
std::pair<PcSamplingRecordT*, size_t> userdata;
userdata.first = new PcSamplingRecordT[TOTAL_NUM_SAMPLES];
userdata.second = TOTAL_NUM_SAMPLES;
latch->sync();
auto t0 = std::chrono::system_clock::now();
CHECK_PARSER(_parse_buffer<GFX9>(
(generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
[](rocprofiler_pc_sampling_record_t** sample, uint64_t size, void* userdata_) {
auto* pair =
reinterpret_cast<std::pair<rocprofiler_pc_sampling_record_t*, size_t>*>(userdata_);
*sample = pair->first;
user_callback_t<PcSamplingRecordT> user_cb =
[](PcSamplingRecordT** sample, uint64_t size, void* userdata_) {
auto* pair = reinterpret_cast<std::pair<PcSamplingRecordT*, size_t>*>(userdata_);
*sample = pair->first;
return size;
},
&userdata,
&corr_map));
};
auto t0 = std::chrono::system_clock::now();
CHECK_PARSER(_parse_buffer<GFX9>((generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
user_cb,
&userdata,
&corr_map));
auto t1 = std::chrono::system_clock::now();
delete[] userdata.first;
return {TOTAL_NUM_SAMPLES, (t1 - t0).count()};
}
template <typename PcSamplingRecordT>
void
multithread_codeobj(size_t tid, Latch* latch)
{
@@ -215,11 +225,11 @@ multithread_codeobj(size_t tid, Latch* latch)
constexpr int NUM_SAMPLES = 50;
constexpr int QSIZE = 16;
auto buffer = std::make_shared<MockRuntimeBuffer>(tid);
auto queue = std::make_shared<MockQueue>(QSIZE, buffer);
auto buffer = std::make_shared<MockRuntimeBuffer<PcSamplingRecordT>>(tid);
auto queue = std::make_shared<MockQueue<PcSamplingRecordT>>(QSIZE, buffer);
std::pair<rocprofiler_pc_sampling_record_t*, size_t> userdata;
userdata.first = new rocprofiler_pc_sampling_record_t[NUM_SAMPLES];
std::pair<PcSamplingRecordT*, size_t> userdata;
userdata.first = new PcSamplingRecordT[NUM_SAMPLES];
userdata.second = NUM_SAMPLES;
latch->sync();
@@ -227,7 +237,7 @@ multithread_codeobj(size_t tid, Latch* latch)
for(int d = 0; d < NUM_DISPATCH; d++)
{
buffer->packets.clear();
auto dispatch = std::make_shared<MockDispatch>(queue);
auto dispatch = std::make_shared<MockDispatch<PcSamplingRecordT>>(queue);
const size_t pc_base_addr = NUM_SAMPLES * dispatch->unique_id;
table->insert(addr_range_t{pc_base_addr, NUM_SAMPLES, dispatch->unique_id});
@@ -242,25 +252,25 @@ multithread_codeobj(size_t tid, Latch* latch)
dispatch->submit(uni);
}
CHECK_PARSER(_parse_buffer<GFX9>(
(generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
[](rocprofiler_pc_sampling_record_t** sample, uint64_t size, void* userdata_) {
auto* pair =
reinterpret_cast<std::pair<rocprofiler_pc_sampling_record_t*, size_t>*>(
userdata_);
*sample = pair->first;
user_callback_t<PcSamplingRecordT> user_cb =
[](PcSamplingRecordT** sample, uint64_t size, void* userdata_) {
auto* pair = reinterpret_cast<std::pair<PcSamplingRecordT*, size_t>*>(userdata_);
*sample = pair->first;
assert(size <= NUM_SAMPLES);
return size;
},
&userdata,
&corr_map));
};
CHECK_PARSER(_parse_buffer<GFX9>((generic_sample_t*) buffer->packets.data(),
buffer->packets.size(),
user_cb,
&userdata,
&corr_map));
for(int s = 0; s < NUM_SAMPLES; s++)
{
const auto& pc = userdata.first[s].pc;
EXPECT_EQ(pc.loaded_code_object_id, dispatch->unique_id);
EXPECT_EQ(pc.loaded_code_object_offset, s);
EXPECT_EQ(pc.code_object_id, dispatch->unique_id);
EXPECT_EQ(pc.code_object_offset, s);
}
table->remove(addr_range_t{pc_base_addr, NUM_SAMPLES, dispatch->unique_id});
@@ -269,7 +279,9 @@ multithread_codeobj(size_t tid, Latch* latch)
delete[] userdata.first;
}
TEST(pcs_parser, bench_test)
template <typename PcSamplingRecordT>
void
pcs_parser_bench_test()
{
size_t time = 0;
size_t samples = 0;
@@ -280,7 +292,8 @@ TEST(pcs_parser, bench_test)
std::vector<std::future<std::pair<size_t, size_t>>> threads{};
for(size_t t = 0; t < NUM_THREADS; t++)
threads.push_back(std::async(std::launch::async, MultiThread_BenchMark, t, &latch));
threads.push_back(std::async(
std::launch::async, MultiThread_BenchMark<PcSamplingRecordT>, t, &latch));
if(it == 0) continue; // Skip warmup
@@ -295,23 +308,47 @@ TEST(pcs_parser, bench_test)
double mean = 1E3 * NUM_THREADS * samples / time;
std::cout << "Benchmark: Parsed " << int(mean * 1E3 + 0.5) * 1E-3f << " Msample/s (";
std::cout << int(sizeof(rocprofiler_pc_sampling_record_t) * mean) << " MB/s)" << std::endl;
std::cout << int(sizeof(PcSamplingRecordT) * mean) << " MB/s)" << std::endl;
};
TEST(pcs_parser, bench_test)
{
pcs_parser_bench_test<rocprofiler_pc_sampling_record_host_trap_v0_t>();
pcs_parser_bench_test<rocprofiler_pc_sampling_record_stochastic_v0_t>();
}
template <typename PcSamplingRecordT>
void
pcs_parser_hammer_test()
{
Latch latch(NUM_THREADS);
std::vector<std::future<void>> threads{};
for(size_t i = 0; i < NUM_THREADS; i++)
threads.push_back(
std::async(std::launch::async, multithread_queue_hammer<PcSamplingRecordT>, i, &latch));
};
TEST(pcs_parser, hammer_test)
{
Latch latch(NUM_THREADS);
pcs_parser_hammer_test<rocprofiler_pc_sampling_record_host_trap_v0_t>();
pcs_parser_hammer_test<rocprofiler_pc_sampling_record_stochastic_v0_t>();
}
std::vector<std::future<void>> threads{};
for(size_t i = 0; i < NUM_THREADS; i++)
threads.push_back(std::async(std::launch::async, multithread_queue_hammer, i, &latch));
};
TEST(pcs_parser, codeobj_test)
template <typename PcSamplingRecordT>
void
pcs_parser_codeobj_test()
{
Latch latch(NUM_THREADS);
std::vector<std::future<void>> threads{};
for(size_t i = 0; i < NUM_THREADS; i++)
threads.push_back(std::async(std::launch::async, multithread_codeobj, i, &latch));
};
threads.push_back(
std::async(std::launch::async, multithread_codeobj<PcSamplingRecordT>, i, &latch));
}
TEST(pcs_parser, codeobj_test)
{
pcs_parser_codeobj_test<rocprofiler_pc_sampling_record_host_trap_v0_t>();
pcs_parser_codeobj_test<rocprofiler_pc_sampling_record_stochastic_v0_t>();
}