/* * 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 #include #include #include #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. \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; }