blob: aa69a617ea6ad4968298b34af31cce93b6e350ab [file] [log] [blame]
/*
* Copyright 2024 Intel Corporation
* SPDX-License-Identifier: MIT
*
* Purpose:
* Enables EU stall sampling available in Xe2+ Intel GPUs. Enables
* GPU sampling of EU stalls. Every N mircoseconds, program collects
* sampled data from GPU. Accumulated data dumped to stdout once
* intel_monitor is closed.
*/
#include <fcntl.h>
#include <getopt.h>
#include <termios.h>
#include <xf86drm.h>
#include "intel_monitor_eustall.h"
#include "dev/intel_device_info.h"
static int
get_drm_device(struct intel_device_info *devinfo)
{
drmDevicePtr devices[8];
int max_devices = drmGetDevices2(0, devices, 8);
int i, fd = -1;
for (i = 0; i < max_devices; i++) {
if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
devices[i]->bustype == DRM_BUS_PCI &&
devices[i]->deviceinfo.pci->vendor_id == 0x8086) {
fd = open(devices[i]->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC);
if (fd < 0)
continue;
if (!intel_get_device_info_from_fd(fd, devinfo, -1, -1)) {
close(fd);
fd = -1;
continue;
}
/* Found a device! */
break;
}
}
return fd;
}
/* Keyboard hit function
*
* Return true if keypress detected.
*/
static bool
kbhit()
{
int ch, oldf;
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if (ch != EOF) {
ungetc(ch, stdin);
return true;
}
return false;
}
static void
print_help(const char *progname, FILE *file)
{
fprintf(file,
"\n"
"Usage: %s [OPTION]...\n"
"Sample and collate GPU metrics across system. Studies workloads run in parallel.\n"
" -h, --help display this help\n"
" -e, --eustall sample eu stalls. Supported Xe2+.\n"
" -f, --file name of output file. DEFAULT=stdout\n"
" -t, --sample_time period of sampling, in microseconds. DEFAULT=1000\n"
"\n"
"Eu stall sample usage:\n"
" 0. Run `sudo sysctl dev.xe.observation_paranoid=0`\n"
" 1. Launch gfx app with INTEL_DEBUG=shaders-lineno. Redirect stderr to asm.txt.\n"
" 2. When gfx app ready to monitor, begin capturing eustall data by launching\n"
" `intel_monitor -e -f eustall.csv` in separate console.\n"
" 3. When enough data has been collected, close intel_monitor by pressing any key.\n"
" 4. Correlate eustall data in eustall.csv with shader instructions in asm.txt by\n"
" matching instruction offsets. Use data to determine which instructions are\n"
" stalling and why.\n"
"\n"
"Eu stall defintions:\n"
"tdr_count - Number of cycles EU stalled, with at least one thread waiting\n"
" on Pixel Shader dependency. Multiple stall reasons can\n"
" qualify during the same cycle.\n"
"other_count - Number of cycles EU stalled, with at least one thread waiting\n"
" on any other dependency (Flag/EoT etc). Multiple stall reasons\n"
" can qualify during the same cycle.\n"
"control_count - Number of cycles EU stalled, with at least one thread waiting\n"
" for JEU to complete branch instruction. Multiple stall reasons\n"
" can qualify during the same cycle.\n"
"pipestall_count - Number of cycles EU stalled, with at least one thread ready to\n"
" be scheduled (Grf conf/send holds etc). Multiple stall reasons\n"
" can qualify during the same cycle.\n"
"send_count - Number of cycles EU stalled, with at least one thread waiting\n"
" for SEND message to be dispatched from EU. Multiple stall\n"
" reasons can qualify during the same cycle.\n"
"dist_acc_count - Number of cycles EU stalled, with at least one thread waiting\n"
" for ALU to write GRF/ACC register. Multiple stall reasons can\n"
" qualify during the same cycle.\n"
"sbid_count - Number of cycles EU stalled, with at least one thread waiting\n"
" for Scoreboard token to be available. Multiple stall reasons\n"
" can qualify during the same cycle.\n"
"sync_count - Number of cycles EU stalled, with at least one thread waiting\n"
" for Gateway to write Notify register. Multiple stall reasons\n"
" can qualify during the same cycle.\n"
"inst_fetch_count - Number of cycles EU stalled, with at least one thread waiting\n"
" for Instruction Fetch. Multiple stall reasons can qualify\n"
" during the same cycle.\n"
"active_count - Number of cycles no EU stalled.\n\n",
progname);
}
enum intel_monitor_sampling_mode
{
/* No sampling requested */
INTEL_MONITOR_MODE_NONE = 0,
/* Sample eu stalls */
INTEL_MONITOR_MODE_EUSTALL = 1
};
int
main(int argc, char *argv[])
{
int c, i;
FILE *fd_out = stdout;
uint64_t sample_period_us = 1000;
bool success = true;
void *cfg = NULL;
bool (*do_sample)(void*) = NULL;
void (*do_dump_results)(void*, FILE*) = NULL;
void (*do_close)(void*) = NULL;
enum intel_monitor_sampling_mode sample_mode = INTEL_MONITOR_MODE_NONE;
const struct option intel_monitor_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "eustall", no_argument, NULL, 'e' },
{ "file", required_argument, NULL, 'f' },
{ "sample_time", required_argument, NULL, 't' },
{ NULL, 0, NULL, 0 }
};
struct intel_device_info devinfo;
int drm_fd = get_drm_device(&devinfo);
if (drm_fd < 0) {
fprintf(stderr, "IMON: Error encountered while getting drm_device. err=%i\n",
drm_fd);
exit(EXIT_FAILURE);
}
/* Parse arguments */
while ((c = getopt_long(argc, argv, "hef:t:", intel_monitor_opts, &i)) != -1) {
switch (c) {
case 'h':
print_help(argv[0], stderr);
return EXIT_SUCCESS;
case 'e':
sample_mode = INTEL_MONITOR_MODE_EUSTALL;
break;
case 'f':
fd_out = fopen(optarg, "w");
if (!fd_out) {
fprintf(stderr, "IMON: Error opening output file '%s'\n", optarg);
exit(EXIT_FAILURE);
}
break;
case 't':
sample_period_us = atoi(optarg);
break;
default:
fprintf(stderr,
"IMON: Error. Unexpected parameter encountered '%c'.\n", c);
exit(EXIT_FAILURE);
}
}
/* Setup cfg based on selected sampling mode */
if (sample_mode == INTEL_MONITOR_MODE_EUSTALL) {
fprintf(stderr, "IMON: Setting up EU Stall Sampling\n");
if (devinfo.ver < 20) {
fprintf(stderr, "IMON: EU Stall Sampler supported on Xe2+ platforms.\n");
exit(EXIT_FAILURE);
}
cfg = eustall_setup(drm_fd, &devinfo);
do_sample = eustall_sample;
do_dump_results = eustall_dump_results;
do_close = eustall_close;
} else {
fprintf(stderr,
"IMON: Error. No sampling mode set. Modes supported: ['-e']\n");
exit(EXIT_FAILURE);
}
/* Main loop */
fprintf(stderr, "IMON: Collecting samples. <Press any key to stop>\n");
while (!kbhit()) {
if (!success)
exit(EXIT_FAILURE);
success = do_sample(cfg);
usleep(sample_period_us);
}
fprintf(stderr, "IMON: Dumping results\n");
do_dump_results(cfg, fd_out);
do_close(cfg);
fprintf(stderr, "IMON: done\n");
return EXIT_SUCCESS;
}