blob: 74a41c1be850666aaeea972c769998afe0ee95d7 [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/summary.h"
#include <cstdint>
#include <cstring>
#include <vector>
#include "iree/hal/api.h"
#include "iree/testing/gtest.h"
#include "iree/testing/status_matchers.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 MakeDispatchEventsChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.content_type = IREE_HAL_PROFILE_CONTENT_TYPE_DISPATCH_EVENTS;
return chunk;
}
static iree_hal_profile_file_record_t MakeHostExecutionEventsChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.content_type = IREE_HAL_PROFILE_CONTENT_TYPE_HOST_EXECUTION_EVENTS;
return chunk;
}
static iree_hal_profile_file_record_t MakeClockCorrelationsChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.content_type = IREE_HAL_PROFILE_CONTENT_TYPE_CLOCK_CORRELATIONS;
return chunk;
}
static iree_hal_profile_file_record_t MakeDeviceMetricSourcesChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.content_type = IREE_HAL_PROFILE_CONTENT_TYPE_DEVICE_METRIC_SOURCES;
return chunk;
}
static iree_hal_profile_file_record_t MakeDeviceMetricDescriptorsChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.content_type = IREE_HAL_PROFILE_CONTENT_TYPE_DEVICE_METRIC_DESCRIPTORS;
return chunk;
}
static iree_hal_profile_file_record_t MakeDeviceMetricSamplesChunk(
const std::vector<uint8_t>& payload) {
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.content_type = IREE_HAL_PROFILE_CONTENT_TYPE_DEVICE_METRIC_SAMPLES;
return chunk;
}
template <typename T>
static void AppendPlainRecord(std::vector<uint8_t>* payload, const T& record) {
const iree_host_size_t offset = payload->size();
payload->resize(offset + sizeof(record));
memcpy(payload->data() + offset, &record, sizeof(record));
}
template <typename T>
static void AppendInlineRecord(std::vector<uint8_t>* payload, const T& record,
iree_const_byte_span_t inline_payload) {
const iree_host_size_t offset = payload->size();
payload->resize(offset + sizeof(record) + inline_payload.data_length);
memcpy(payload->data() + offset, &record, sizeof(record));
memcpy(payload->data() + offset + sizeof(record), inline_payload.data,
inline_payload.data_length);
}
TEST(ProfileSummaryTest, AccumulatesDroppedRecordsFromTruncatedChunks) {
std::vector<uint8_t> payload;
iree_hal_profile_file_record_t chunk = MakeChunk(payload);
chunk.header.record_type = IREE_HAL_PROFILE_FILE_RECORD_TYPE_CHUNK;
chunk.header.chunk_flags = IREE_HAL_PROFILE_CHUNK_FLAG_TRUNCATED;
chunk.header.dropped_record_count = 7;
iree_profile_summary_t summary;
iree_profile_summary_initialize(iree_allocator_system(), &summary);
IREE_ASSERT_OK(iree_profile_summary_process_record(&summary, &chunk));
EXPECT_EQ(1u, summary.truncated_chunk_count);
EXPECT_EQ(7u, summary.dropped_record_count);
iree_profile_summary_deinitialize(&summary);
}
TEST(ProfileSummaryTest, RejectsMalformedHostExecutionEventRecord) {
std::vector<uint8_t> payload(sizeof(uint32_t), 0);
iree_hal_profile_file_record_t chunk = MakeHostExecutionEventsChunk(payload);
iree_profile_summary_t summary;
iree_profile_summary_initialize(iree_allocator_system(), &summary);
IREE_EXPECT_STATUS_IS(IREE_STATUS_DATA_LOSS,
iree_profile_summary_process_record(&summary, &chunk));
EXPECT_EQ(1u, summary.chunk_count);
EXPECT_EQ(1u, summary.host_execution_event_chunk_count);
EXPECT_EQ(0u, summary.host_execution_event_record_count);
EXPECT_EQ(0u, summary.invalid_host_execution_event_record_count);
EXPECT_EQ(0u, summary.total_host_execution_duration_ns);
iree_profile_summary_deinitialize(&summary);
}
TEST(ProfileSummaryTest, CountsInvalidClockAlignmentSamples) {
std::vector<uint8_t> payload;
iree_hal_profile_clock_correlation_record_t clock_sample =
iree_hal_profile_clock_correlation_record_default();
clock_sample.flags =
IREE_HAL_PROFILE_CLOCK_CORRELATION_FLAG_DEVICE_TICK |
IREE_HAL_PROFILE_CLOCK_CORRELATION_FLAG_HOST_TIME_BRACKET;
clock_sample.sample_id = 1;
clock_sample.physical_device_ordinal = 2;
clock_sample.device_tick = 1000;
clock_sample.host_time_begin_ns = 10;
clock_sample.host_time_end_ns = 12;
AppendPlainRecord(&payload, clock_sample);
clock_sample.flags |=
IREE_HAL_PROFILE_CLOCK_CORRELATION_FLAG_DEVICE_TICK_UNALIGNED;
clock_sample.sample_id = 2;
clock_sample.device_tick = 2000;
clock_sample.host_time_begin_ns = 20;
clock_sample.host_time_end_ns = 22;
AppendPlainRecord(&payload, clock_sample);
iree_hal_profile_file_record_t clock_chunk =
MakeClockCorrelationsChunk(payload);
iree_profile_summary_t summary;
iree_profile_summary_initialize(iree_allocator_system(), &summary);
IREE_ASSERT_OK(iree_profile_summary_process_record(&summary, &clock_chunk));
EXPECT_EQ(1u, summary.clock_correlation_chunk_count);
ASSERT_EQ(1u, summary.device_count);
const iree_profile_device_summary_t* device = &summary.devices[0];
EXPECT_EQ(2u, device->physical_device_ordinal);
EXPECT_EQ(2u, device->clock_sample_count);
EXPECT_EQ(1u, device->invalid_clock_alignment_sample_count);
EXPECT_EQ(1u, device->first_clock_sample.sample_id);
EXPECT_EQ(2u, device->last_clock_sample.sample_id);
iree_profile_summary_deinitialize(&summary);
}
TEST(ProfileSummaryTest, RecordsFirstNonOkSessionEndStatus) {
iree_hal_profile_file_record_t first_session_end;
memset(&first_session_end, 0, sizeof(first_session_end));
first_session_end.header.record_type =
IREE_HAL_PROFILE_FILE_RECORD_TYPE_SESSION_END;
first_session_end.header.session_id = 42;
first_session_end.header.stream_id = 7;
first_session_end.header.event_id = 8;
first_session_end.header.session_status_code = IREE_STATUS_CANCELLED;
iree_hal_profile_file_record_t second_session_end = first_session_end;
second_session_end.header.session_id = 99;
second_session_end.header.session_status_code =
IREE_STATUS_RESOURCE_EXHAUSTED;
iree_profile_summary_t summary;
iree_profile_summary_initialize(iree_allocator_system(), &summary);
IREE_ASSERT_OK(
iree_profile_summary_process_record(&summary, &first_session_end));
IREE_ASSERT_OK(
iree_profile_summary_process_record(&summary, &second_session_end));
EXPECT_EQ(2u, summary.session_end_count);
EXPECT_EQ(2u, summary.non_ok_session_end_count);
EXPECT_EQ(42u, summary.first_non_ok_session_id);
EXPECT_EQ(7u, summary.first_non_ok_stream_id);
EXPECT_EQ(8u, summary.first_non_ok_event_id);
EXPECT_EQ(IREE_STATUS_CANCELLED, summary.first_non_ok_session_status_code);
iree_profile_summary_deinitialize(&summary);
}
TEST(ProfileSummaryTest, RecordsDeviceMetricMetadataAndSamples) {
const char source_name[] = "summary.metrics";
iree_hal_profile_device_metric_source_record_t source_record =
iree_hal_profile_device_metric_source_record_default();
source_record.source_id = 1;
source_record.physical_device_ordinal = 2;
source_record.device_class = IREE_HAL_PROFILE_DEVICE_CLASS_GPU;
source_record.metric_count = 1;
source_record.name_length = sizeof(source_name) - 1;
source_record.record_length =
sizeof(source_record) + source_record.name_length;
std::vector<uint8_t> source_payload;
AppendInlineRecord(
&source_payload, source_record,
iree_make_const_byte_span(source_name, source_record.name_length));
const char metric_name[] = "summary.metric";
const char metric_description[] = "Summary metric.";
iree_hal_profile_device_metric_descriptor_record_t descriptor_record =
iree_hal_profile_device_metric_descriptor_record_default();
descriptor_record.source_id = source_record.source_id;
descriptor_record.metric_id = IREE_HAL_PROFILE_METRIC_ID_PRODUCER_BASE + 7;
descriptor_record.unit = IREE_HAL_PROFILE_METRIC_UNIT_COUNT;
descriptor_record.value_kind = IREE_HAL_PROFILE_METRIC_VALUE_KIND_U64;
descriptor_record.semantic = IREE_HAL_PROFILE_METRIC_SEMANTIC_INSTANT;
descriptor_record.plot_hint = IREE_HAL_PROFILE_METRIC_PLOT_HINT_NUMBER;
descriptor_record.name_length = sizeof(metric_name) - 1;
descriptor_record.description_length = sizeof(metric_description) - 1;
descriptor_record.record_length = sizeof(descriptor_record) +
descriptor_record.name_length +
descriptor_record.description_length;
std::vector<uint8_t> descriptor_payload;
AppendInlineRecord(
&descriptor_payload, descriptor_record,
iree_make_const_byte_span(metric_name, descriptor_record.name_length));
descriptor_payload.insert(
descriptor_payload.end(), metric_description,
metric_description + descriptor_record.description_length);
iree_hal_profile_device_metric_sample_record_t sample_record =
iree_hal_profile_device_metric_sample_record_default();
sample_record.sample_id = 1;
sample_record.source_id = source_record.source_id;
sample_record.physical_device_ordinal = source_record.physical_device_ordinal;
sample_record.host_time_begin_ns = 100;
sample_record.host_time_end_ns = 110;
sample_record.value_count = 1;
sample_record.record_length =
sizeof(sample_record) + sizeof(iree_hal_profile_device_metric_value_t);
iree_hal_profile_device_metric_value_t sample_value = {};
sample_value.metric_id = descriptor_record.metric_id;
sample_value.value_bits = 42;
sample_value.flags = IREE_HAL_PROFILE_DEVICE_METRIC_VALUE_FLAG_NONE;
std::vector<uint8_t> sample_payload;
AppendInlineRecord(
&sample_payload, sample_record,
iree_make_const_byte_span(&sample_value, sizeof(sample_value)));
iree_profile_summary_t summary;
iree_profile_summary_initialize(iree_allocator_system(), &summary);
iree_hal_profile_file_record_t source_chunk =
MakeDeviceMetricSourcesChunk(source_payload);
iree_hal_profile_file_record_t descriptor_chunk =
MakeDeviceMetricDescriptorsChunk(descriptor_payload);
iree_hal_profile_file_record_t sample_chunk =
MakeDeviceMetricSamplesChunk(sample_payload);
IREE_ASSERT_OK(iree_profile_summary_process_record(&summary, &source_chunk));
IREE_ASSERT_OK(
iree_profile_summary_process_record(&summary, &descriptor_chunk));
IREE_ASSERT_OK(iree_profile_summary_process_record(&summary, &sample_chunk));
EXPECT_EQ(1u, summary.device_metric.source_record_count);
EXPECT_EQ(1u, summary.device_metric.descriptor_record_count);
EXPECT_EQ(1u, summary.device_metric.sample_record_count);
EXPECT_EQ(1u, summary.device_metric.value_count);
ASSERT_EQ(1u, summary.device_count);
EXPECT_EQ(2u, summary.devices[0].physical_device_ordinal);
EXPECT_EQ(1u, summary.devices[0].device_metric.sample_count);
EXPECT_EQ(1u, summary.devices[0].device_metric.value_count);
EXPECT_EQ(0u, summary.devices[0].device_metric.invalid_sample_count);
iree_profile_summary_deinitialize(&summary);
}
TEST(ProfileSummaryTest, SeparatesHostDurationsFromDeviceDispatchTicks) {
std::vector<uint8_t> dispatch_payload;
iree_hal_profile_dispatch_event_t dispatch =
iree_hal_profile_dispatch_event_default();
dispatch.start_tick = 10;
dispatch.end_tick = 30;
AppendPlainRecord(&dispatch_payload, dispatch);
dispatch.start_tick = 50;
dispatch.end_tick = 40;
AppendPlainRecord(&dispatch_payload, dispatch);
iree_hal_profile_file_record_t dispatch_chunk =
MakeDispatchEventsChunk(dispatch_payload);
dispatch_chunk.header.physical_device_ordinal = 2;
std::vector<uint8_t> host_execution_payload;
iree_hal_profile_host_execution_event_t host_execution =
iree_hal_profile_host_execution_event_default();
host_execution.start_host_time_ns = 100;
host_execution.end_host_time_ns = 175;
AppendPlainRecord(&host_execution_payload, host_execution);
host_execution.start_host_time_ns = 200;
host_execution.end_host_time_ns = 150;
AppendPlainRecord(&host_execution_payload, host_execution);
iree_hal_profile_file_record_t host_execution_chunk =
MakeHostExecutionEventsChunk(host_execution_payload);
iree_profile_summary_t summary;
iree_profile_summary_initialize(iree_allocator_system(), &summary);
IREE_ASSERT_OK(
iree_profile_summary_process_record(&summary, &dispatch_chunk));
IREE_ASSERT_OK(
iree_profile_summary_process_record(&summary, &host_execution_chunk));
ASSERT_EQ(1u, summary.device_count);
const iree_profile_device_summary_t* device = &summary.devices[0];
EXPECT_EQ(2u, device->physical_device_ordinal);
EXPECT_EQ(2u, device->dispatch_event_count);
EXPECT_EQ(1u, device->invalid_dispatch_event_count);
EXPECT_EQ(20u, device->total_dispatch_ticks);
EXPECT_EQ(0u, summary.queue_device_event_record_count);
EXPECT_EQ(2u, summary.host_execution_event_record_count);
EXPECT_EQ(1u, summary.invalid_host_execution_event_record_count);
EXPECT_EQ(75u, summary.total_host_execution_duration_ns);
iree_profile_summary_deinitialize(&summary);
}
} // namespace