[PATCH i-g-t v11 5/5] tools/gputop/gputop: Enable support for multiple GPUs and instances
Purkait, Soham
soham.purkait at intel.com
Wed Jun 11 07:11:00 UTC 2025
On 05-06-2025 22:27, Riana Tauro wrote:
> Hi Soham
>
> Code looks good. Have one question and few minor comments below
>
> On 5/22/2025 9:14 PM, Soham Purkait wrote:
>> Introduce vendor-agnostic support for
>> handling multiple GPUs and instances
>> in gputop. Improve the tool's adaptability
>> to various GPU configurations.
>>
>> v2 : Fix for refactoring GPUTOP into a
>> vendor-agnostic tool. (Lucas)
>>
>> v3 : New year included in copyright. (Kamil, Riana)
>> Removed caps in function name. (Riana)
>> Struct for driver specific operations. (Riana)
>> Headers in alphabetical order. (Kamil, Riana)
>>
>> v4 : Commit description and signed-off included.
>>
>> v5 : Fix for proper resource cleanup. (Riana)
>> Use "dev_type" enum for card_type. (Krzysztof)
>> Add new filter to return collection
>> of matching devices. (Zbigniew)
>>
>> v6 : Use device filter to populate the array of
>> cards for all supported drivers. (Zbigniew)
>>
>> v7 : Use filter to find all the cards. (Zbigniew)
>>
>> v8 : Removed 'drivers' array parameter from
>> card match function. (Zbigniew)
>>
>> v10 : Resolved 'populate_devices' call with
>> pci subsystem filtering. (Zbigniew)
>>
>> v11 : Add space after /* and before */ for better
>> readability. (Zbigniew)
>> Comments wrapped at 75/100. (Riana)
>>
>> Signed-off-by: Soham Purkait <soham.purkait at intel.com>
>>
>> Acked-by: Zbigniew Kempczyński <zbigniew.kempczynski at intel.com>
>> ---
>> tools/{ => gputop}/gputop.c | 223 +++++++++++++++++++++++++++++-------
>> tools/gputop/meson.build | 6 +
>> tools/meson.build | 6 +-
>> 3 files changed, 191 insertions(+), 44 deletions(-)
>> rename tools/{ => gputop}/gputop.c (66%)
>> create mode 100644 tools/gputop/meson.build
>>
>> diff --git a/tools/gputop.c b/tools/gputop/gputop.c
>> similarity index 66%
>> rename from tools/gputop.c
>> rename to tools/gputop/gputop.c
>> index 43b01f566..fc3ccb83b 100644
>> --- a/tools/gputop.c
>> +++ b/tools/gputop/gputop.c
>> @@ -1,6 +1,6 @@
>> // SPDX-License-Identifier: MIT
>> /*
>> - * Copyright © 2023 Intel Corporation
>> + * Copyright © 2023-2025 Intel Corporation
>> */
>> #include <assert.h>
>> @@ -14,66 +14,145 @@
>> #include <math.h>
>> #include <poll.h>
>> #include <signal.h>
>> +#include <stdbool.h>
>> #include <stdint.h>
>> #include <stdio.h>
>> #include <stdlib.h>
>> #include <string.h>
>> +#include <sys/sysmacros.h>
>
> alphabetical
>
>> #include <sys/ioctl.h>
>> #include <sys/stat.h>
>> #include <sys/types.h>
>> -#include <unistd.h>
>> #include <termios.h>
>> -#include <sys/sysmacros.h>
>> -#include <stdbool.h>
>> +#include <unistd.h>
>> +#include "drmtest.h"
>> #include "igt_core.h"
>> #include "igt_drm_clients.h"
>> #include "igt_drm_fdinfo.h"
>> +#include "igt_perf.h"
>> #include "igt_profiling.h"
>> -#include "drmtest.h"
>> +#include "xe_gputop.h"
>> +#include "xe/xe_query.h"
>> +
>> +/**
>> + * Supported Drivers
>> + *
>> + * Adhere to the following requirements when implementing support
>> for the
>> + * new driver:
>> + * @drivers: Update drivers[] with driver string.
>> + * @total_count: Update NUM_DRIVER with the total number of
>> supported drivers.
>> + * @operations: Update the respective operations of the new driver:
>> + * gputop_init,
>> + * discover_engines,
>> + * pmu_init,
>> + * pmu_sample,
>> + * print_engines,
>> + * clean_up
>> + * @devices: Update devices[] array of type "struct gputop_device"
>> with the
>> + * initial values.
>> + */
>> +static const char * const drivers[] = {
>> + "xe",
>> + /* Keep the last one as NULL */
>> + NULL
>> +};
>> +
>> +/**
>> + * Number of supported drivers needs to be adjusted as per the
>> length of
>> + * the drivers[] array.
>> + */
>> +#define NUM_DRIVER 1
>> +
>> +/**
>> + * Supported operations on driver instances. Update the oprs[] array
>> for
>> + * each individual driver specific function. Maintain the sequence
>> as per
>> + * drivers[] array.
>> + */
>> +struct device_operations oprs[NUM_DRIVER] = {
>
> Replace oprs with ops.. generally use ops for operations
Isn't ops more sound like options ?
>> + {
>> + xe_gputop_init,
>> + xe_populate_engines,
>> + xe_pmu_init,
>> + xe_pmu_sample,
>> + xe_print_engines,
>> + xe_clean_up
>> + }
>> +};
>> +
>> +/*
>> + * devices[] array of type struct gputop_device
>> + */
>> +struct gputop_device devices[] = {
>
> I must have commented here before. As i see this is one entry per
> driver right? why is it named device?
Actually this is keeping track of the devices for each driver, so the name.
I have updated the description for better readability.
>
>> + {false, 0, NULL}
>> +};
>> enum utilization_type {
>> UTILIZATION_TYPE_ENGINE_TIME,
>> UTILIZATION_TYPE_TOTAL_CYCLES,
>> };
>> -static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊",
>> "▉", "█" };
>> -
>> -#define ANSI_HEADER "\033[7m"
>> -#define ANSI_RESET "\033[0m"
>> -
>> -static void n_spaces(const unsigned int n)
>> +static void gputop_clean_up(void)
>> {
>> - unsigned int i;
>> -
>> - for (i = 0; i < n; i++)
>> - putchar(' ');
>> + for (int i = 0; drivers[i]; i++) {
>> + oprs[i].clean_up(devices[i].instances, devices[i].len);
>> + free(devices[i].instances);
>> + devices[i].driver_present = false;
>> + devices[i].len = 0;
>> + }
>> }
>> -static void print_percentage_bar(double percent, int max_len)
>> +static int find_driver(struct igt_device_card *card)
>> {
>> - int bar_len, i, len = max_len - 1;
>> - const int w = 8;
>> -
>> - len -= printf("|%5.1f%% ", percent);
>> -
>> - /* no space left for bars, do what we can */
>> - if (len < 0)
>> - len = 0;
>> -
>> - bar_len = ceil(w * percent * len / 100.0);
>> - if (bar_len > w * len)
>> - bar_len = w * len;
>> + for (int i = 0; drivers[i]; i++) {
>> + if (strcmp(drivers[i], card->driver) == 0)
>> + return i;
>> + }
>> + return -1;
>> +}
>> - for (i = bar_len; i >= w; i -= w)
>> - printf("%s", bars[w]);
>> - if (i)
>> - printf("%s", bars[i]);
>> +/*
>> + * If filter is not NULL i will be ignored.
>> + */
>> +static int populate_device_instances(const char *filter)
>> +{
>> + struct igt_device_card *cards = NULL;
>> + struct igt_device_card *card_inplace = NULL;
>> + struct gputop_device *dev = NULL;
>> + int driver_no;
>> + int count, final_count = 0;
>
> %s/final_count/card_count
Here count and final_count both are keeping track of the cards but
final_count is the count of supported filtered devices, so the name.
>
>> +
>> + count = igt_device_card_match_all(filter, &cards);
>> + for (int j = 0; j < count; j++) {
>> + if (strcmp((cards + j)->subsystem, "pci") != 0)
>> + continue;
>> - len -= (bar_len + (w - 1)) / w;
>> - n_spaces(len);
>> + driver_no = find_driver(cards + j);
>> + if (driver_no < 0)
>> + continue;
>> - putchar('|');
>> + dev = devices + driver_no;
>> + if (!dev->driver_present)
>> + dev->driver_present = true;
>> + dev->len++;
>> + dev->instances = realloc(dev->instances,
>> + dev->len * sizeof(struct xe_gputop));
>> + if (!dev->instances) {
>> + fprintf(stderr,
>> + "Device instance realloc failed (%s)\n",
>> + strerror(errno));
>> + exit(EXIT_FAILURE);
>> + }
>> + card_inplace = (struct igt_device_card *)
>> + calloc(1, sizeof(struct igt_device_card));
>> + memcpy(card_inplace, cards + j, sizeof(struct
>> igt_device_card));
>> + oprs[driver_no].gputop_init((struct xe_gputop
>> *)(dev->instances + dev->len - 1),
>> + card_inplace);
>> + final_count++;
>> + }
>> + if (count)
>> + free(cards);
>> + return final_count;
>> }
>> static int
>> @@ -333,6 +412,7 @@ static void clrscr(void)
>> struct gputop_args {
>> long n_iter;
>> unsigned long delay_usec;
>> + char *device;
>> };
>> static void help(void)
>> @@ -343,16 +423,18 @@ static void help(void)
>> "\t-h, --help show this help\n"
>> "\t-d, --delay =SEC[.TENTHS] iterative delay as SECS
>> [.TENTHS]\n"
>> "\t-n, --iterations =NUMBER number of executions\n"
>> + "\t-D, --device Device filter\n"
>> , program_invocation_short_name);
>> }
>> static int parse_args(int argc, char * const argv[], struct
>> gputop_args *args)
>> {
>> - static const char cmdopts_s[] = "hn:d:";
>> + static const char cmdopts_s[] = "hn:d:D:";
>> static const struct option cmdopts[] = {
>> {"help", no_argument, 0, 'h'},
>> {"delay", required_argument, 0, 'd'},
>> {"iterations", required_argument, 0, 'n'},
>> + {"device", required_argument, 0, 'D'},
>> { }
>> };
>> @@ -360,6 +442,7 @@ static int parse_args(int argc, char * const
>> argv[], struct gputop_args *args)
>> memset(args, 0, sizeof(*args));
>> args->n_iter = -1;
>> args->delay_usec = 2 * USEC_PER_SEC;
>> + args->device = NULL;
>> for (;;) {
>> int c, idx = 0;
>> @@ -383,6 +466,9 @@ static int parse_args(int argc, char * const
>> argv[], struct gputop_args *args)
>> return -1;
>> }
>> break;
>> + case 'D':
>> + args->device = optarg;
>> + break;
>> case 'h':
>> help();
>> return 0;
>> @@ -422,6 +508,52 @@ int main(int argc, char **argv)
>> n = args.n_iter;
>> period_us = args.delay_usec;
>> + if (!populate_device_instances(args.device ? args.device
>> + : "device:subsystem=pci,card=all")) {
>> + printf("No device found.\n");
>> + gputop_clean_up();
>> + exit(1);
>> + }
>> +
>> + for (int i = 0; drivers[i]; i++) {
>> + if (devices[i].driver_present) {
>
> if not present continue
It will be at the end of the outer loop if not present so it eventually
continues.
>
> Thanks
> Riana
>
>> + for (int j = 0; j < devices[i].len; j++) {
>> + if (!oprs[i].init_engines(devices[i].instances + j)) {
>> + fprintf(stderr,
>> + "Failed to initialize engines! (%s)\n",
>> + strerror(errno));
>> + gputop_clean_up();
>> + return EXIT_FAILURE;
>> + }
>> + ret = oprs[i].pmu_init(devices[i].instances + j);
>> +
>> + if (ret) {
>> + fprintf(stderr,
>> + "Failed to initialize PMU! (%s)\n",
>> + strerror(errno));
>> + if (errno == EACCES && geteuid())
>> + fprintf(stderr,
>> + "\n"
>> + "When running as a normal user
>> CAP_PERFMON is required to access performance\n"
>> + "monitoring. See \"man 7 capabilities\",
>> \"man 8 setcap\", or contact your\n"
>> + "distribution vendor for assistance.\n"
>> + "\n"
>> + "More information can be found at 'Perf
>> events and tool security' document:\n"
>> +
>> "https://www.kernel.org/doc/html/latest/admin-guide/perf-security.html\n");
>> +
>> + igt_devices_free();
>> + gputop_clean_up();
>> + return EXIT_FAILURE;
>> + }
>> + }
>> + }
>> + }
>> +
>> + for (int i = 0; drivers[i]; i++) {
>> + for (int j = 0; devices[i].driver_present && j <
>> devices[i].len; j++)
>> + oprs[i].pmu_sample(devices[i].instances + j);
>> + }
>> +
>> clients = igt_drm_clients_init(NULL);
>> if (!clients)
>> exit(1);
>> @@ -442,14 +574,27 @@ int main(int argc, char **argv)
>> while ((n != 0) && !stop_top) {
>> struct igt_drm_client *c, *prevc = NULL;
>> - int i, engine_w = 0, lines = 0;
>> + int k, engine_w = 0, lines = 0;
>> igt_drm_clients_scan(clients, NULL, NULL, 0, NULL, 0);
>> +
>> + for (int i = 0; drivers[i]; i++) {
>> + for (int j = 0; devices[i].driver_present && j <
>> devices[i].len; j++)
>> + oprs[i].pmu_sample(devices[i].instances + j);
>> + }
>> +
>> igt_drm_clients_sort(clients, client_cmp);
>> update_console_size(&con_w, &con_h);
>> clrscr();
>> + for (int i = 0; drivers[i]; i++) {
>> + for (int j = 0; devices[i].driver_present && j <
>> devices[i].len; j++) {
>> + lines = oprs[i].print_engines(devices[i].instances + j,
>> + lines, con_w, con_h);
>> + }
>> + }
>> +
>> if (!clients->num_clients) {
>> const char *msg = " (No GPU clients yet. Start workload
>> to see stats)";
>> @@ -457,7 +602,7 @@ int main(int argc, char **argv)
>> (int)(con_w - strlen(msg) - 1), msg);
>> }
>> - igt_for_each_drm_client(clients, c, i) {
>> + igt_for_each_drm_client(clients, c, k) {
>> assert(c->status != IGT_DRM_CLIENT_PROBE);
>> if (c->status != IGT_DRM_CLIENT_ALIVE)
>> break; /* Active clients are first in the array. */
>> @@ -481,11 +626,11 @@ int main(int argc, char **argv)
>> }
>> igt_drm_clients_free(clients);
>> + gputop_clean_up();
>> if (profiled_devices != NULL) {
>> igt_devices_configure_profiling(profiled_devices, false);
>> igt_devices_free_profiling(profiled_devices);
>> }
>> -
>> return 0;
>> }
>> diff --git a/tools/gputop/meson.build b/tools/gputop/meson.build
>> new file mode 100644
>> index 000000000..4766d8496
>> --- /dev/null
>> +++ b/tools/gputop/meson.build
>> @@ -0,0 +1,6 @@
>> +gputop_src = [ 'gputop.c', 'utils.c', 'xe_gputop.c']
>> +executable('gputop', sources : gputop_src,
>> + install : true,
>> + install_rpath : bindir_rpathdir,
>> + dependencies :
>> [igt_deps,lib_igt_perf,lib_igt_drm_clients,lib_igt_drm_fdinfo,lib_igt_profiling,math],
>> + install: true)
>> diff --git a/tools/meson.build b/tools/meson.build
>> index de866c392..8002f707d 100644
>> --- a/tools/meson.build
>> +++ b/tools/meson.build
>> @@ -69,11 +69,6 @@ if libudev.found()
>> install : true)
>> endif
>> -executable('gputop', 'gputop.c',
>> - install : true,
>> - install_rpath : bindir_rpathdir,
>> - dependencies :
>> [lib_igt_drm_clients,lib_igt_drm_fdinfo,lib_igt_profiling,math])
>> -
>> intel_l3_parity_src = [ 'intel_l3_parity.c',
>> 'intel_l3_udev_listener.c' ]
>> executable('intel_l3_parity', sources : intel_l3_parity_src,
>> dependencies : tool_deps,
>> @@ -122,3 +117,4 @@ endif
>> subdir('i915-perf')
>> subdir('xe-perf')
>> subdir('null_state_gen')
>> +subdir('gputop')
>
More information about the igt-dev
mailing list