blob: 7cc5d9476da73dc19e7fe2a72a4d6d182e3de1f3 [file]
// Copyright 2026 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/tooling/profile/counter.h"
#include <cstdint>
#include <cstring>
#include <initializer_list>
#include <vector>
#include "iree/testing/gtest.h"
#include "iree/testing/status_matchers.h"
#include "iree/tooling/profile/model.h"
namespace {
static iree_hal_profile_file_record_t MakeChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk;
memset(&chunk, 0, sizeof(chunk));
chunk.header.record_type = IREE_HAL_PROFILE_FILE_RECORD_TYPE_CHUNK;
chunk.content_type = IREE_SV("application/vnd.iree.test");
chunk.payload = iree_make_const_byte_span(payload.data(), payload.size());
return chunk;
}
static iree_hal_profile_file_record_t MakeCounterSetsChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.content_type = IREE_HAL_PROFILE_CONTENT_TYPE_COUNTER_SETS;
return chunk;
}
static iree_hal_profile_file_record_t MakeCountersChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.content_type = IREE_HAL_PROFILE_CONTENT_TYPE_COUNTERS;
return chunk;
}
static iree_hal_profile_file_record_t MakeCounterSamplesChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.content_type = IREE_HAL_PROFILE_CONTENT_TYPE_COUNTER_SAMPLES;
return chunk;
}
static void AppendCounterSet(std::vector<uint8_t>* payload,
uint64_t counter_set_id, uint32_t counter_count,
uint32_t sample_value_count,
iree_string_view_t name) {
iree_hal_profile_counter_set_record_t record =
iree_hal_profile_counter_set_record_default();
record.record_length = (uint32_t)(sizeof(record) + name.size);
record.counter_set_id = counter_set_id;
record.physical_device_ordinal = 0;
record.counter_count = counter_count;
record.sample_value_count = sample_value_count;
record.name_length = (uint32_t)name.size;
const iree_host_size_t offset = payload->size();
payload->resize(offset + record.record_length);
memcpy(payload->data() + offset, &record, sizeof(record));
memcpy(payload->data() + offset + sizeof(record), name.data, name.size);
}
static void AppendCounter(std::vector<uint8_t>* payload,
uint64_t counter_set_id, uint32_t counter_ordinal,
uint32_t sample_value_offset,
iree_string_view_t block_name,
iree_string_view_t name) {
iree_hal_profile_counter_record_t record =
iree_hal_profile_counter_record_default();
record.record_length =
(uint32_t)(sizeof(record) + block_name.size + name.size);
record.flags = IREE_HAL_PROFILE_COUNTER_FLAG_RAW;
record.unit = IREE_HAL_PROFILE_COUNTER_UNIT_COUNT;
record.physical_device_ordinal = 0;
record.counter_set_id = counter_set_id;
record.counter_ordinal = counter_ordinal;
record.sample_value_offset = sample_value_offset;
record.sample_value_count = 1;
record.block_name_length = (uint32_t)block_name.size;
record.name_length = (uint32_t)name.size;
const iree_host_size_t offset = payload->size();
payload->resize(offset + record.record_length);
uint8_t* target = payload->data() + offset;
memcpy(target, &record, sizeof(record));
target += sizeof(record);
memcpy(target, block_name.data, block_name.size);
target += block_name.size;
memcpy(target, name.data, name.size);
}
static void AppendCounterSampleRecord(
std::vector<uint8_t>* payload,
const iree_hal_profile_counter_sample_record_t& base_sample,
std::initializer_list<uint64_t> values) {
iree_hal_profile_counter_sample_record_t sample = base_sample;
sample.record_length =
(uint32_t)(sizeof(sample) + values.size() * sizeof(uint64_t));
sample.sample_value_count = (uint32_t)values.size();
const iree_host_size_t offset = payload->size();
payload->resize(offset + sample.record_length);
memcpy(payload->data() + offset, &sample, sizeof(sample));
memcpy(payload->data() + offset + sizeof(sample), values.begin(),
values.size() * sizeof(uint64_t));
}
static void AppendDispatchCounterSample(
std::vector<uint8_t>* payload, uint64_t sample_id, uint64_t counter_set_id,
std::initializer_list<uint64_t> values) {
iree_hal_profile_counter_sample_record_t sample =
iree_hal_profile_counter_sample_record_default();
sample.flags = IREE_HAL_PROFILE_COUNTER_SAMPLE_FLAG_DISPATCH_EVENT |
IREE_HAL_PROFILE_COUNTER_SAMPLE_FLAG_DEVICE_TICK_RANGE;
sample.scope = IREE_HAL_PROFILE_COUNTER_SAMPLE_SCOPE_DISPATCH;
sample.sample_id = sample_id;
sample.counter_set_id = counter_set_id;
sample.dispatch_event_id = 27;
sample.start_tick = 100;
sample.end_tick = 132;
sample.physical_device_ordinal = 0;
sample.queue_ordinal = 0;
AppendCounterSampleRecord(payload, sample, values);
}
typedef struct counter_sample_collector_t {
// Number of counter value rows observed by the callback.
uint64_t row_count;
// Number of callback rows from dispatch-scoped samples.
uint64_t dispatch_row_count;
// Number of callback rows from physical-device time-range samples.
uint64_t device_time_range_row_count;
// Number of callback rows whose samples had device tick ranges.
uint64_t device_tick_range_row_count;
// Last callback row device-tick range start.
uint64_t last_start_tick;
// Last callback row device-tick range end.
uint64_t last_end_tick;
// Sum of callback row counter values.
double value_sum;
// True when any callback row came from a truncated source chunk.
bool saw_truncated_chunk;
} counter_sample_collector_t;
static iree_status_t CollectCounterSample(
void* user_data, const iree_profile_counter_sample_row_t* row) {
counter_sample_collector_t* collector =
static_cast<counter_sample_collector_t*>(user_data);
++collector->row_count;
if (row->sample->scope == IREE_HAL_PROFILE_COUNTER_SAMPLE_SCOPE_DISPATCH) {
++collector->dispatch_row_count;
} else if (row->sample->scope ==
IREE_HAL_PROFILE_COUNTER_SAMPLE_SCOPE_DEVICE_TIME_RANGE) {
++collector->device_time_range_row_count;
}
if (iree_all_bits_set(
row->sample->flags,
IREE_HAL_PROFILE_COUNTER_SAMPLE_FLAG_DEVICE_TICK_RANGE)) {
++collector->device_tick_range_row_count;
collector->last_start_tick = row->sample->start_tick;
collector->last_end_tick = row->sample->end_tick;
}
collector->value_sum += row->value_sum;
collector->saw_truncated_chunk |= row->is_truncated;
return iree_ok_status();
}
TEST(ProfileCounterTest, InvokesSampleCallbackForMatchedValues) {
std::vector<uint8_t> counter_set_payload;
AppendCounterSet(&counter_set_payload, 9, 2, 2, IREE_SV("test_set"));
iree_hal_profile_file_record_t counter_set_chunk =
MakeCounterSetsChunk(counter_set_payload);
std::vector<uint8_t> counter_payload;
AppendCounter(&counter_payload, 9, 0, 0, IREE_SV("SQ"), IREE_SV("WAVES"));
AppendCounter(&counter_payload, 9, 1, 1, IREE_SV("SQ"), IREE_SV("BUSY"));
iree_hal_profile_file_record_t counter_chunk =
MakeCountersChunk(counter_payload);
std::vector<uint8_t> sample_payload;
AppendDispatchCounterSample(&sample_payload, 5, 9, {7, 11});
iree_hal_profile_file_record_t sample_chunk =
MakeCounterSamplesChunk(sample_payload);
sample_chunk.header.chunk_flags = IREE_HAL_PROFILE_CHUNK_FLAG_TRUNCATED;
iree_profile_model_t model;
iree_profile_model_initialize(iree_allocator_system(), &model);
iree_profile_counter_context_t context;
iree_profile_counter_context_initialize(iree_allocator_system(), &context);
IREE_ASSERT_OK(iree_profile_counter_process_metadata_record(
&context, &counter_set_chunk));
IREE_ASSERT_OK(
iree_profile_counter_process_metadata_record(&context, &counter_chunk));
counter_sample_collector_t collector = {0};
const iree_profile_counter_sample_callback_t sample_callback = {
CollectCounterSample,
&collector,
};
IREE_ASSERT_OK(iree_profile_counter_process_sample_records(
&context, &model, &sample_chunk, IREE_SV("*"), -1, sample_callback));
EXPECT_EQ(1u, context.total_sample_count);
EXPECT_EQ(1u, context.matched_sample_count);
EXPECT_EQ(1u, context.truncated_sample_count);
EXPECT_EQ(2u, context.aggregate_count);
EXPECT_EQ(2u, collector.row_count);
EXPECT_EQ(2u, collector.dispatch_row_count);
EXPECT_EQ(18.0, collector.value_sum);
EXPECT_EQ(2u, collector.device_tick_range_row_count);
EXPECT_EQ(100u, collector.last_start_tick);
EXPECT_EQ(132u, collector.last_end_tick);
EXPECT_TRUE(collector.saw_truncated_chunk);
iree_profile_counter_context_deinitialize(&context);
iree_profile_model_deinitialize(&model);
}
TEST(ProfileCounterTest, ProcessesDeviceTimeRangeSamplesWithoutDispatch) {
std::vector<uint8_t> counter_set_payload;
AppendCounterSet(&counter_set_payload, 10, 1, 1, IREE_SV("device_set"));
iree_hal_profile_file_record_t counter_set_chunk =
MakeCounterSetsChunk(counter_set_payload);
std::vector<uint8_t> counter_payload;
AppendCounter(&counter_payload, 10, 0, 0, IREE_SV("GRBM"),
IREE_SV("GUI_ACTIVE"));
iree_hal_profile_file_record_t counter_chunk =
MakeCountersChunk(counter_payload);
iree_hal_profile_counter_sample_record_t sample =
iree_hal_profile_counter_sample_record_default();
sample.flags = IREE_HAL_PROFILE_COUNTER_SAMPLE_FLAG_DEVICE_TICK_RANGE;
sample.scope = IREE_HAL_PROFILE_COUNTER_SAMPLE_SCOPE_DEVICE_TIME_RANGE;
sample.sample_id = 6;
sample.counter_set_id = 10;
sample.start_tick = 200;
sample.end_tick = 260;
sample.physical_device_ordinal = 0;
std::vector<uint8_t> sample_payload;
AppendCounterSampleRecord(&sample_payload, sample, {19});
iree_hal_profile_file_record_t sample_chunk =
MakeCounterSamplesChunk(sample_payload);
iree_profile_model_t model;
iree_profile_model_initialize(iree_allocator_system(), &model);
iree_profile_counter_context_t context;
iree_profile_counter_context_initialize(iree_allocator_system(), &context);
IREE_ASSERT_OK(iree_profile_counter_process_metadata_record(
&context, &counter_set_chunk));
IREE_ASSERT_OK(
iree_profile_counter_process_metadata_record(&context, &counter_chunk));
counter_sample_collector_t collector = {0};
const iree_profile_counter_sample_callback_t sample_callback = {
CollectCounterSample,
&collector,
};
IREE_ASSERT_OK(iree_profile_counter_process_sample_records(
&context, &model, &sample_chunk, IREE_SV("*"), -1, sample_callback));
EXPECT_EQ(1u, context.total_sample_count);
EXPECT_EQ(1u, context.matched_sample_count);
EXPECT_EQ(1u, context.aggregate_count);
EXPECT_EQ(1u, collector.row_count);
EXPECT_EQ(0u, collector.dispatch_row_count);
EXPECT_EQ(1u, collector.device_time_range_row_count);
EXPECT_EQ(1u, collector.device_tick_range_row_count);
EXPECT_EQ(200u, collector.last_start_tick);
EXPECT_EQ(260u, collector.last_end_tick);
EXPECT_EQ(19.0, collector.value_sum);
iree_profile_counter_context_deinitialize(&context);
iree_profile_model_deinitialize(&model);
}
} // namespace