blob: 936f3cde4129ce7ea0e813ee38c07e5cf8d39510 [file] [log] [blame]
/*
* Copyright 2024 Intel Corporation
* SPDX-License-Identifier: MIT
*/
#include "intel_monitor_eustall.h"
#include <poll.h>
#include "util/u_qsort.h"
#include "util/xxhash.h"
static bool
oa_stream_ready(int fd)
{
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
pfd.revents = 0;
if (poll(&pfd, 1, 0) < 0) {
fprintf(stderr, "IMON: Error polling OA stream\n");
return false;
}
if (!(pfd.revents & POLLIN))
return false;
return true;
}
/* Initialize eustall_cfg and enable eu stall profiling by
* opening stream with KMD. Return eustall_cfg.
*/
struct eustall_config*
eustall_setup(int drm_fd, struct intel_device_info *devinfo)
{
struct eustall_config *eustall_cfg = malloc(sizeof(struct eustall_config));
eustall_cfg->min_event_count = 1, /* min records to trigger data flush */
eustall_cfg->drm_fd = drm_fd;
eustall_cfg->fd = -1;
eustall_cfg->devinfo = devinfo;
eustall_cfg->result.accumulator =
_mesa_hash_table_create(NULL,
_mesa_hash_u64,
_mesa_key_u64_equal);
/* Arbitrarily large buffer for copying stall data into. */
eustall_cfg->buf_len = 64 * 1024 * 1024;
eustall_cfg->buf = malloc(eustall_cfg->buf_len);
return eustall_cfg;
}
static bool
init_stream(struct eustall_config *eustall_cfg)
{
eustall_cfg->record_size =
intel_perf_eustall_stream_record_size(eustall_cfg->devinfo,
eustall_cfg->drm_fd);
if (eustall_cfg->record_size <= 0) {
fprintf(stderr, "IMON: ERROR encountered querying record size."
" err=%i\n", eustall_cfg->record_size);
return false;
}
eustall_cfg->sample_rate =
intel_perf_eustall_stream_sample_rate(eustall_cfg->devinfo,
eustall_cfg->drm_fd);
if (eustall_cfg->sample_rate <= 0) {
fprintf(stderr, "IMON: ERROR encountered querying sampling rate."
" err=%i\n", eustall_cfg->sample_rate);
return false;
}
eustall_cfg->fd =
intel_perf_eustall_stream_open(eustall_cfg->devinfo,
eustall_cfg->drm_fd,
eustall_cfg->sample_rate,
eustall_cfg->min_event_count);
if (eustall_cfg->fd < 0) {
fprintf(stderr, "IMON: ERROR encountered while opening "
"eustall stream. err=%i\n", eustall_cfg->fd);
return false;
}
fprintf(stderr, "IMON: intel_perf_eustall_stream_open = %i\n",
eustall_cfg->fd);
int err = intel_perf_eustall_stream_set_state(eustall_cfg->devinfo,
eustall_cfg->fd, true);
if (err != 0) {
fprintf(stderr, "IMON: ERROR encountered while enabling stream."
" err=%i\n", err);
return false;
}
return true;
}
/* Sample all eustall data via KMD stream. Open stream on first call.
*/
bool
eustall_sample(void *cfg)
{
struct eustall_config *eustall_cfg = cfg;
bool overflow;
if (eustall_cfg->fd < 0 || eustall_cfg->record_size <= 0)
return init_stream(eustall_cfg);
while (oa_stream_ready(eustall_cfg->fd)) {
int bytes_read =
intel_perf_eustall_stream_read_samples(eustall_cfg->devinfo,
eustall_cfg->fd,
eustall_cfg->buf,
eustall_cfg->buf_len,
&overflow);
if (bytes_read <= 0) {
if (bytes_read < 0)
fprintf(stderr, "IMON: read_samples returned err=%i\n", bytes_read);
break;
}
if (overflow)
fprintf(stderr, "IMON: detected EU stall sampling buffer overflow. "
"Some stall data was lost.\n");
intel_perf_eustall_accumulate_results(&eustall_cfg->result,
eustall_cfg->buf,
eustall_cfg->buf + bytes_read,
eustall_cfg->record_size);
}
return true;
}
static int
compare_ip_addr(const void *a, const void *b)
{
const uint64_t *val_a = a;
const uint64_t *val_b = b;
return (int)(*val_a - *val_b);
}
/* Write all previously collected results to fd. Clear results.
*/
void
eustall_dump_results(void *cfg, FILE *file)
{
struct eustall_config *eustall_cfg = cfg;
struct hash_table *accumulator = eustall_cfg->result.accumulator;
uint64_t *ip_addr_keys = malloc(accumulator->size * sizeof(uint64_t));
uint32_t num_entries = accumulator->entries;
uint32_t i = 0;
/* Sort keys so ip_addr appear in order */
hash_table_foreach(accumulator, entry) {
struct intel_perf_query_eustall_event* data =
(struct intel_perf_query_eustall_event*)entry->data;
ip_addr_keys[i++] = data->ip_addr;
}
qsort(ip_addr_keys, accumulator->entries, sizeof(uint64_t), compare_ip_addr);
fprintf(file, "offset,tdr_count,other_count,control_count,pipestall_count,"
"send_count,dist_acc_count,sbid_count,sync_count,"
"inst_fetch_count,active_count,sum\n");
for (i = 0; i < num_entries; i++) {
struct hash_entry *entry =
_mesa_hash_table_search(accumulator, ip_addr_keys + i);
struct intel_perf_query_eustall_event *data =
(struct intel_perf_query_eustall_event*)entry->data;
uint64_t ip_addr = data->ip_addr << 3;
uint64_t sum = data->tdr_count + data->other_count +
data->control_count + data->pipestall_count + data->send_count +
data->dist_acc_count + data->sbid_count + data->sync_count +
data->inst_fetch_count + data->active_count;
fprintf(file,
"0x%08" PRIx64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ","
"%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ","
"%" PRIu64 ",%" PRIu64 "\n",
ip_addr, data->tdr_count, data->other_count, data->control_count,
data->pipestall_count, data->send_count, data->dist_acc_count,
data->sbid_count, data->sync_count, data->inst_fetch_count,
data->active_count, sum);
free(data);
_mesa_hash_table_remove(accumulator, entry);
}
free(ip_addr_keys);
}
static void
delete_entry(struct hash_entry *entry)
{
free(entry->data);
}
/* Close eustall stream and deconstruct eustall cfg.
*/
void
eustall_close(void *cfg)
{
struct eustall_config *eustall_cfg = cfg;
_mesa_hash_table_destroy(eustall_cfg->result.accumulator, delete_entry);
eustall_cfg->result.accumulator = NULL;
close(eustall_cfg->fd);
eustall_cfg->fd = -1;
free(eustall_cfg->buf);
eustall_cfg->buf = NULL;
free(eustall_cfg);
}