/* * Copyright 2024 Intel Corporation * SPDX-License-Identifier: MIT */ #include "intel_monitor_eustall.h" #include #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); }