64b5ee3001
Fixes: 610ad8d3 ("intel/tools: create intel_monitor for sampling eu stalls")
Reviewed-by: Felix DeGrood <felix.j.degrood@intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/34439>
220 lines
6.8 KiB
C
220 lines
6.8 KiB
C
/*
|
|
* 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);
|
|
}
|