blob: b3ed97f09d549219abfac2fe386adb3b764de7f2 [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/statistics.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "iree/hal/api.h"
#include "iree/hal/utils/statistics_sink.h"
#include "iree/tooling/profile/common.h"
#include "iree/tooling/profile/reader.h"
typedef struct iree_profile_statistics_context_t {
// Aggregate sink receiving CHUNK records from the profile file.
iree_hal_profile_statistics_sink_t* statistics_sink;
} iree_profile_statistics_context_t;
typedef struct iree_profile_statistics_jsonl_context_t {
// Aggregate sink owning the row currently being printed.
const iree_hal_profile_statistics_sink_t* statistics_sink;
// Output stream receiving JSONL rows.
FILE* file;
} iree_profile_statistics_jsonl_context_t;
static const char* iree_profile_statistics_row_type_name(
iree_hal_profile_statistics_row_type_t row_type) {
switch (row_type) {
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_DISPATCH_FUNCTION:
return "dispatch_function";
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_DISPATCH_COMMAND_BUFFER:
return "dispatch_command_buffer";
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_DISPATCH_COMMAND_OPERATION:
return "dispatch_command_operation";
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_QUEUE_DEVICE_OPERATION:
return "queue_device_operation";
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_QUEUE_HOST_OPERATION:
return "queue_host_operation";
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_HOST_EXECUTION_FUNCTION:
return "host_execution_function";
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_HOST_EXECUTION_COMMAND_BUFFER:
return "host_execution_command_buffer";
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_HOST_EXECUTION_COMMAND_OPERATION:
return "host_execution_command_operation";
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_HOST_EXECUTION_QUEUE_OPERATION:
return "host_execution_queue_operation";
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_MEMORY_LIFECYCLE:
return "memory_lifecycle";
default:
return "unknown";
}
}
static const char* iree_profile_statistics_time_domain_name(
iree_hal_profile_statistics_time_domain_t time_domain) {
switch (time_domain) {
case IREE_HAL_PROFILE_STATISTICS_TIME_DOMAIN_DEVICE_TICK:
return "device_tick";
case IREE_HAL_PROFILE_STATISTICS_TIME_DOMAIN_IREE_HOST_TIME_NS:
return "iree_host_time_ns";
default:
return "none";
}
}
static const char* iree_profile_statistics_duration_unit_name(
iree_hal_profile_statistics_time_domain_t time_domain) {
switch (time_domain) {
case IREE_HAL_PROFILE_STATISTICS_TIME_DOMAIN_DEVICE_TICK:
return "device_tick";
case IREE_HAL_PROFILE_STATISTICS_TIME_DOMAIN_IREE_HOST_TIME_NS:
return "ns";
default:
return "none";
}
}
static const char* iree_profile_statistics_queue_event_type_name(
iree_hal_profile_queue_event_type_t type) {
switch (type) {
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_BARRIER:
return "barrier";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_DISPATCH:
return "dispatch";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_EXECUTE:
return "execute";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_COPY:
return "copy";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_FILL:
return "fill";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_UPDATE:
return "update";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_READ:
return "read";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_WRITE:
return "write";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_ALLOCA:
return "alloca";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_DEALLOCA:
return "dealloca";
case IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_HOST_CALL:
return "host_call";
default:
return "unknown";
}
}
static const char* iree_profile_statistics_memory_event_type_name(
iree_hal_profile_memory_event_type_t type) {
switch (type) {
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_SLAB_ACQUIRE:
return "slab_acquire";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_SLAB_RELEASE:
return "slab_release";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_POOL_RESERVE:
return "pool_reserve";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_POOL_MATERIALIZE:
return "pool_materialize";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_POOL_RELEASE:
return "pool_release";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_POOL_WAIT:
return "pool_wait";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_QUEUE_ALLOCA:
return "queue_alloca";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_QUEUE_DEALLOCA:
return "queue_dealloca";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_BUFFER_ALLOCATE:
return "buffer_allocate";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_BUFFER_FREE:
return "buffer_free";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_BUFFER_IMPORT:
return "buffer_import";
case IREE_HAL_PROFILE_MEMORY_EVENT_TYPE_BUFFER_UNIMPORT:
return "buffer_unimport";
default:
return "unknown";
}
}
static const char* iree_profile_statistics_event_type_name(
const iree_hal_profile_statistics_row_t* row) {
switch (row->row_type) {
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_QUEUE_DEVICE_OPERATION:
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_QUEUE_HOST_OPERATION:
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_HOST_EXECUTION_QUEUE_OPERATION:
return iree_profile_statistics_queue_event_type_name(
(iree_hal_profile_queue_event_type_t)row->event_type);
case IREE_HAL_PROFILE_STATISTICS_ROW_TYPE_MEMORY_LIFECYCLE:
return iree_profile_statistics_memory_event_type_name(
(iree_hal_profile_memory_event_type_t)row->event_type);
default:
return "";
}
}
static iree_hal_profile_chunk_metadata_t
iree_profile_statistics_metadata_from_record(
const iree_hal_profile_file_record_t* record) {
iree_hal_profile_chunk_metadata_t metadata =
iree_hal_profile_chunk_metadata_default();
metadata.content_type = record->content_type;
metadata.name = record->name;
metadata.session_id = record->header.session_id;
metadata.stream_id = record->header.stream_id;
metadata.event_id = record->header.event_id;
metadata.executable_id = record->header.executable_id;
metadata.command_buffer_id = record->header.command_buffer_id;
metadata.physical_device_ordinal = record->header.physical_device_ordinal;
metadata.queue_ordinal = record->header.queue_ordinal;
metadata.flags = record->header.chunk_flags;
metadata.dropped_record_count = record->header.dropped_record_count;
return metadata;
}
static iree_status_t iree_profile_statistics_process_record(
void* user_data, const iree_hal_profile_file_record_t* record,
iree_host_size_t record_index) {
(void)record_index;
// Aggregate the full bundle. The statistics sink resets on begin_session,
// so replay only chunk records instead of forwarding session markers.
if (record->header.record_type != IREE_HAL_PROFILE_FILE_RECORD_TYPE_CHUNK) {
return iree_ok_status();
}
iree_profile_statistics_context_t* context =
(iree_profile_statistics_context_t*)user_data;
const iree_hal_profile_chunk_metadata_t metadata =
iree_profile_statistics_metadata_from_record(record);
const iree_const_byte_span_t* iovecs =
record->payload.data_length != 0 ? &record->payload : NULL;
return iree_hal_profile_sink_write(
iree_hal_profile_statistics_sink_base(context->statistics_sink),
&metadata, iovecs ? 1 : 0, iovecs);
}
static iree_status_t iree_profile_statistics_load_file(
iree_string_view_t path,
iree_hal_profile_statistics_sink_t* statistics_sink,
iree_allocator_t host_allocator) {
iree_profile_file_t profile_file;
IREE_RETURN_IF_ERROR(
iree_profile_file_open(path, host_allocator, &profile_file));
iree_profile_statistics_context_t context = {
.statistics_sink = statistics_sink,
};
const iree_profile_file_record_callback_t callback = {
.fn = iree_profile_statistics_process_record,
.user_data = &context,
};
iree_status_t status =
iree_profile_file_for_each_record(&profile_file, callback);
iree_profile_file_close(&profile_file);
return status;
}
static void iree_profile_statistics_fprint_duration_ns_or_null(
FILE* file, const iree_hal_profile_statistics_sink_t* statistics_sink,
const iree_hal_profile_statistics_row_t* row, const char* field_name,
uint64_t duration) {
fprintf(file, ",\"%s\":", field_name);
if (!iree_all_bits_set(row->flags,
IREE_HAL_PROFILE_STATISTICS_ROW_FLAG_TIMING)) {
fprintf(file, "null");
return;
}
uint64_t duration_ns = 0;
if (iree_hal_profile_statistics_sink_scale_duration_to_ns(
statistics_sink, row, duration, &duration_ns)) {
fprintf(file, "%" PRIu64, duration_ns);
} else {
fprintf(file, "null");
}
}
static iree_status_t iree_profile_statistics_print_jsonl_row(
void* user_data, const iree_hal_profile_statistics_row_t* row) {
const iree_profile_statistics_jsonl_context_t* context =
(const iree_profile_statistics_jsonl_context_t*)user_data;
FILE* file = context->file;
const bool has_timing = iree_all_bits_set(
row->flags, IREE_HAL_PROFILE_STATISTICS_ROW_FLAG_TIMING);
const bool has_payload_bytes = iree_all_bits_set(
row->flags, IREE_HAL_PROFILE_STATISTICS_ROW_FLAG_PAYLOAD_BYTES);
const bool has_operation_count = iree_all_bits_set(
row->flags, IREE_HAL_PROFILE_STATISTICS_ROW_FLAG_OPERATION_COUNT);
const bool has_tile_totals = iree_all_bits_set(
row->flags, IREE_HAL_PROFILE_STATISTICS_ROW_FLAG_TILE_TOTALS);
fprintf(file, "{\"type\":\"statistics_row\",\"row_type\":");
iree_profile_fprint_json_string(
file, iree_make_cstring_view(
iree_profile_statistics_row_type_name(row->row_type)));
fprintf(file, ",\"row_type_value\":%u", row->row_type);
fprintf(file, ",\"time_domain\":");
iree_profile_fprint_json_string(
file, iree_make_cstring_view(
iree_profile_statistics_time_domain_name(row->time_domain)));
fprintf(file, ",\"time_domain_value\":%u", row->time_domain);
fprintf(file, ",\"duration_unit\":");
iree_profile_fprint_json_string(
file, iree_make_cstring_view(
iree_profile_statistics_duration_unit_name(row->time_domain)));
fprintf(file,
",\"flags\":%u,\"has_timing\":%s,\"has_payload_bytes\":%s"
",\"has_operation_count\":%s,\"has_tile_totals\":%s",
row->flags, has_timing ? "true" : "false",
has_payload_bytes ? "true" : "false",
has_operation_count ? "true" : "false",
has_tile_totals ? "true" : "false");
fprintf(file,
",\"physical_device_ordinal\":%u,\"queue_ordinal\":%u"
",\"event_type\":%u,\"event_name\":",
row->physical_device_ordinal, row->queue_ordinal, row->event_type);
const char* event_type_name = iree_profile_statistics_event_type_name(row);
if (event_type_name[0] != '\0') {
iree_profile_fprint_json_string(file,
iree_make_cstring_view(event_type_name));
} else {
fprintf(file, "null");
}
fprintf(file,
",\"executable_id\":%" PRIu64 ",\"command_buffer_id\":%" PRIu64
",\"function_ordinal\":%u,\"command_index\":%u",
row->executable_id, row->command_buffer_id, row->function_ordinal,
row->command_index);
iree_string_view_t function_name = iree_string_view_empty();
const bool has_function_name =
iree_hal_profile_statistics_sink_find_function_name(
context->statistics_sink, row->executable_id, row->function_ordinal,
&function_name);
fprintf(file, ",\"function_name\":");
if (has_function_name) {
iree_profile_fprint_json_string(file, function_name);
} else {
fprintf(file, "null");
}
fprintf(file,
",\"sample_count\":%" PRIu64 ",\"invalid_sample_count\":%" PRIu64
",\"operation_count\":%" PRIu64 ",\"payload_bytes\":%" PRIu64
",\"tile_count\":%" PRIu64 ",\"tile_duration_sum_ns\":%" PRIu64,
row->sample_count, row->invalid_sample_count, row->operation_count,
row->payload_bytes, row->tile_count, row->tile_duration_sum_ns);
fprintf(file,
",\"first_start_time\":%" PRIu64 ",\"last_end_time\":%" PRIu64
",\"total_duration\":%" PRIu64 ",\"minimum_duration\":%" PRIu64
",\"maximum_duration\":%" PRIu64,
has_timing ? row->first_start_time : 0,
has_timing ? row->last_end_time : 0, row->total_duration,
has_timing ? row->minimum_duration : 0,
has_timing ? row->maximum_duration : 0);
iree_profile_statistics_fprint_duration_ns_or_null(
file, context->statistics_sink, row, "total_duration_ns",
row->total_duration);
iree_profile_statistics_fprint_duration_ns_or_null(
file, context->statistics_sink, row, "minimum_duration_ns",
row->minimum_duration);
iree_profile_statistics_fprint_duration_ns_or_null(
file, context->statistics_sink, row, "maximum_duration_ns",
row->maximum_duration);
fprintf(file, "}\n");
return iree_ok_status();
}
static iree_status_t iree_profile_statistics_print_jsonl(
FILE* file, const iree_hal_profile_statistics_sink_t* statistics_sink) {
fprintf(
file,
"{\"type\":\"statistics_summary\",\"row_count\":%" PRIhsz
",\"dropped_record_count\":%" PRIu64 "}\n",
iree_hal_profile_statistics_sink_row_count(statistics_sink),
iree_hal_profile_statistics_sink_dropped_record_count(statistics_sink));
const iree_profile_statistics_jsonl_context_t context = {
.statistics_sink = statistics_sink,
.file = file,
};
const iree_hal_profile_statistics_row_callback_t callback = {
.fn = iree_profile_statistics_print_jsonl_row,
.user_data = (void*)&context,
};
return iree_hal_profile_statistics_sink_for_each_row(statistics_sink,
callback);
}
iree_status_t iree_profile_statistics_file(iree_string_view_t path,
iree_string_view_t format,
FILE* file,
iree_allocator_t host_allocator) {
iree_hal_profile_statistics_sink_t* statistics_sink = NULL;
IREE_RETURN_IF_ERROR(iree_hal_profile_statistics_sink_create(
host_allocator, &statistics_sink));
iree_status_t status =
iree_profile_statistics_load_file(path, statistics_sink, host_allocator);
if (iree_status_is_ok(status)) {
if (iree_string_view_equal(format, IREE_SV("text"))) {
status = iree_hal_profile_statistics_sink_fprint(file, statistics_sink);
} else if (iree_string_view_equal(format, IREE_SV("jsonl"))) {
status = iree_profile_statistics_print_jsonl(file, statistics_sink);
} else {
status = iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"unsupported statistics format '%.*s'",
(int)format.size, format.data);
}
}
iree_hal_profile_statistics_sink_release(statistics_sink);
return status;
}