[PATCH i-g-t v11 4/5] tools/gputop/xe_gputop: Add gputop support for xe specific devices

Purkait, Soham soham.purkait at intel.com
Wed Jun 11 07:58:24 UTC 2025


On 05-06-2025 22:46, Riana Tauro wrote:
> Hi Soham
>
> Some minor comments
>
> On 5/22/2025 9:14 PM, Soham Purkait wrote:
>> Add gputop support for xe-specific devices. Separate
>> driver-specific code into respective source files.
>>
>> v2 : Fix for refactoring GPUTOP into a
>>       vendor-agnostic tool. (Lucas)
>>
>> v3 : Separate commit. (Kamil)
>>
>> v4 : Headers in alphabetical order
>>       Engines memory allocation at
>>       the beginning all at once.
>>       Removed PMU normalization. (Riana)
>>
>> v5 : Refactor to eliminate redundant
>>       and unused code segments.
>>       Fix for proper resource cleanup. (Riana)
>>
>> v8 : Allocated card structure memory inplace and
>>       accordingly modified the clean up code.
>>
>> v11 : Loop optimization in xe_populate_engines.
>>        Removed short_name.
>>        PMU fds are closed on cleanup.
>>        Removed unnecessary comments. (Riana)
>
> lot of versions maybe squash all the comments into one and start new 
> series. But upto you
>
>>
>> Signed-off-by: Soham Purkait <soham.purkait at intel.com>
>> ---
>>   tools/gputop/xe_gputop.c | 378 +++++++++++++++++++++++++++++++++++++++
>>   tools/gputop/xe_gputop.h |  62 +++++++
>>   2 files changed, 440 insertions(+)
>>   create mode 100644 tools/gputop/xe_gputop.c
>>   create mode 100644 tools/gputop/xe_gputop.h
>>
>> diff --git a/tools/gputop/xe_gputop.c b/tools/gputop/xe_gputop.c
>> new file mode 100644
>> index 000000000..ac3ed76e6
>> --- /dev/null
>> +++ b/tools/gputop/xe_gputop.c
>> @@ -0,0 +1,378 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2025 Intel Corporation
>> + */
>> +
>> +#include "xe_gputop.h"
>> +
>> +#define engine_ptr(engines, n) (&(engines)->engine + (n))
>> +
>> +static void __update_sample(struct xe_pmu_counter *counter, uint64_t 
>> val)
>> +{
>> +    counter->val.prev = counter->val.cur;
>> +    counter->val.cur = val;
>> +}
>> +
>> +static void update_sample(struct xe_pmu_counter *counter, uint64_t 
>> *val)
>> +{
>> +    if (counter->present)
>> +        __update_sample(counter, val[counter->idx]);
>> +}
>> +
>> +static const char *class_display_name(unsigned int class)
>> +{
>> +    switch (class) {
>> +    case DRM_XE_ENGINE_CLASS_RENDER:
>> +        return "Render/3D";
>> +    case DRM_XE_ENGINE_CLASS_COPY:
>> +        return "Blitter";
>> +    case DRM_XE_ENGINE_CLASS_VIDEO_DECODE:
>> +        return "Video";
>> +    case DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE:
>> +        return "VideoEnhance";
>> +    case DRM_XE_ENGINE_CLASS_COMPUTE:
>> +        return "Compute";
>> +    default:
>> +        return "[unknown]";
>> +    }
>> +}
>> +
>> +void xe_clean_up(void *obj, int len)
>> +{
>> +    struct xe_engine *eng;
>> +    struct xe_pmu_counter pmu;
>> +    struct xe_gputop *dev = (struct xe_gputop *)obj;
>
> Use inverted xmas tree
>
>> +
>> +    for (int i = 0; i < len; i++) {
>> +        if ((dev + i)->card)
>> +            free((dev + i)->card);
>> +        if ((dev + i)->eng_obj) {
>> +
>> +            for(int j = 0; j < ((struct xe_pmu_device*)(dev + 
>> i)->eng_obj)->num_engines ; j++) {
>> +                eng = engine_ptr((struct xe_pmu_device*)(dev + 
>> i)->eng_obj, j);
>> +                if (eng->display_name)
>> +                    free(eng->display_name);
>> +
>> +                pmu = eng->engine_active_ticks;
>> +                if (pmu.present)
>> +                    close(pmu.fd);
>> +
>> +                pmu = eng->engine_total_ticks;
>> +                if (pmu.present)
>> +                    close(pmu.fd);
>> +            }
>> +            free(dev->eng_obj);
>> +        }
>> +        if ((dev + i)->pmu_device)
>> +            free(dev->pmu_device);
>> +    }
>> +}
>> +
>> +static char *pmu_name(struct igt_device_card *card)
>> +{
>> +    int card_fd;
>> +    char device[30];
>> +    char *path;
>> +
>> +    if (strlen(card->card))
>> +        card_fd = igt_open_card(card);
>> +    else if (strlen(card->render))
>> +        card_fd = igt_open_render(card);
>> +
>> +    if (card_fd == -1)
>> +        return NULL;
>> +
>> +    xe_perf_device(card_fd, device, sizeof(device));
>> +    path = strdup(device);
>> +    close(card_fd);
>> +    return path;
>> +}
>> +
>> +static int _open_pmu(uint64_t type, unsigned int *cnt, struct 
>> xe_pmu_counter *pmu, int *fd)
>> +{
>> +    int fd__ = igt_perf_open_group(type, pmu->config, *fd);
>> +
>> +    if (fd__ >= 0) {
>> +        if (*fd == -1)
>> +            *fd = fd__;
>> +        pmu->present = true;
>> +        pmu->idx = (*cnt)++;
>> +        pmu->fd = fd__;
>> +    }
>> +
>> +    return fd__;
>> +}
>> +
>> +void xe_gputop_init(void *ptr,
>> +            struct igt_device_card *card)
>> +{
>> +    struct xe_gputop *obj = (struct xe_gputop *)ptr;
>> +
>> +    obj->pmu_device = pmu_name(card);
>> +    if (!obj->pmu_device) {
>> +        fprintf(stderr, "%s : pmu_device path returned NULL", 
>> card->pci_slot_name);
>> +        exit(EXIT_FAILURE);
>> +    }
>> +    obj->card = card;
>> +}
>> +
>> +static int pmu_format_shift(int xe, const char *name)
>> +{
>> +    uint32_t start;
>> +    int format;
>> +    char device[80];
>> +
>> +    format = perf_event_format(xe_perf_device(xe, device, 
>> sizeof(device)),
>> +                   name, &start);
>> +    if (format)
>> +        return 0;
>> +
>> +    return start;
>> +}
>> +
>> +static int engine_cmp(const void *__a, const void *__b)
>> +{
>> +    const struct xe_engine *a = (struct xe_engine *)__a;
>> +    const struct xe_engine *b = (struct xe_engine *)__b;
>> +
>> +    if (a->drm_xe_engine.engine_class != b->drm_xe_engine.engine_class)
>> +        return a->drm_xe_engine.engine_class - 
>> b->drm_xe_engine.engine_class;
>> +    else
>> +        return a->drm_xe_engine.engine_instance - 
>> b->drm_xe_engine.engine_instance;
>> +}
>> +
>> +void *xe_populate_engines(const void *obj)
>> +{
>> +    struct igt_device_card *card = ((struct xe_gputop *)obj)->card;
>> +    struct xe_pmu_device *engines;
>> +    int ret = 0;
>> +    char device[30];
>> +    struct drm_xe_engine_class_instance *hwe;
>> +    int card_fd;
>> +    uint64_t engine_class, engine_instance, gt_shift;
>> +    uint64_t engine_active_config, engine_total_config;
>
> Use inverted xmas tree
>
>> +
>> +    if (!card || !strlen(card->card) || !strlen(card->render))
>> +        return NULL;
>> +
>> +    if (strlen(card->card)) {
>> +        card_fd = igt_open_card(card);
>> +    } else if (strlen(card->render)) {
>> +        card_fd = igt_open_render(card);
>> +    } else {
>> +        fprintf(stderr, "Failed to detect device!\n");
>> +        return NULL;
>> +    }
>> +    xe_device_get(card_fd);
>> +    engines = malloc(sizeof(struct xe_pmu_device) +
>> +             xe_number_engines(card_fd) * sizeof(struct xe_engine));
>> +    if (!engines)
>> +        return NULL;
>> +
>> +    memset(engines, 0, sizeof(struct xe_pmu_device) +
>> +           xe_number_engines(card_fd) * sizeof(struct xe_engine));
>> +
>> +    engines->num_engines = 0;
>> +    engines->device = ((struct xe_gputop *)obj)->pmu_device;
>> +    gt_shift = pmu_format_shift(card_fd, "gt");
>> +    engine_class = pmu_format_shift(card_fd, "engine_class");
>> +    engine_instance = pmu_format_shift(card_fd, "engine_instance");
>> +    xe_perf_device(card_fd, device, sizeof(device));
>> +    ret = perf_event_config(device,
>> +                "engine-active-ticks",
>> +                &engine_active_config);
>
> wrap around at 100
>
>> +    if (ret < 0)
>> +        return NULL;
>> +    ret = perf_event_config(device,
>> +                "engine-total-ticks",
>> +                &engine_total_config);
>> +    if (ret < 0)
>> +        return NULL;
>
> add blank line
>
>> +    xe_for_each_engine(card_fd, hwe) {
>> +        uint64_t  param_config;
>> +        struct xe_engine *engine;
>> +
>> +        engine = engine_ptr(engines, engines->num_engines);
>> +        param_config = (uint64_t)hwe->gt_id << gt_shift | 
>> hwe->engine_class << engine_class
>> +            | hwe->engine_instance << engine_instance;
>> +        engine->drm_xe_engine = *hwe;
>> +        engine->engine_active_ticks.config = engine_active_config | 
>> param_config;
>> +        engine->engine_total_ticks.config = engine_total_config | 
>> param_config;
>> +
>> +        if (engine->engine_active_ticks.config == -1 ||
>> +            engine->engine_total_ticks.config == -1) {
>> +            ret = ENOENT;
>> +            break;
>> +        }
>> +
>> +        ret = asprintf(&engine->display_name, "%s/%u",
>> + class_display_name(engine->drm_xe_engine.engine_class),
>> +                   engine->drm_xe_engine.engine_instance);
>> +
>> +        if (ret <= 0) {
>> +            ret = errno;
>> +            break;
>> +        }
>> +
>> +        engines->num_engines++;
>> +    }
>> +
>> +    if (!ret) {
>> +        errno = ret;
>> +        return NULL;
>> +    }
>> +
>> +    qsort(engine_ptr(engines, 0), engines->num_engines,
>> +          sizeof(struct xe_engine), engine_cmp);
>> +
>> +    ((struct xe_gputop *)obj)->eng_obj = engines;
>> +
>> +    return engines;
>> +}
>> +
>> +static uint64_t pmu_read_multi(int fd, unsigned int num, uint64_t *val)
>> +{
>> +    uint64_t buf[2 + num];
>> +    unsigned int i;
>> +    ssize_t len;
>> +
>> +    memset(buf, 0, sizeof(buf));
>> +
>> +    len = read(fd, buf, sizeof(buf));
>> +    assert(len == sizeof(buf));
>> +
>> +    for (i = 0; i < num; i++)
>> +        val[i] = buf[2 + i];
>> +
>> +    return buf[1];
>> +}
>> +
>> +void xe_pmu_sample(const void *obj)
>> +{
>> +    struct xe_pmu_device *engines = ((struct xe_gputop *)obj)->eng_obj;
>> +    const int num_val = engines->num_counters;
>> +    uint64_t val[2 + num_val];
>> +    unsigned int i;
>> +
>> +    pmu_read_multi(engines->fd, num_val, val);
>> +
>> +    for (i = 0; i < engines->num_engines; i++) {
>> +        struct xe_engine *engine = engine_ptr(engines, i);
>> +
>> +        update_sample(&engine->engine_active_ticks, val);
>> +        update_sample(&engine->engine_total_ticks, val);
>> +    }
>> +}
>> +
>> +int xe_pmu_init(const void *obj)
>> +{
>> +    struct xe_pmu_device *engines = ((struct xe_gputop *)obj)->eng_obj;
>> +    unsigned int i;
>> +    int fd;
>> +    struct xe_engine *engine;
>> +    uint64_t type = igt_perf_type_id(engines->device);
>> +
>> +    engines->fd = -1;
>> +    engines->num_counters = 0;
>> +
>> +    for (i = 0; i < engines->num_engines; i++) {
>> +        engine = engine_ptr(engines, i);
>> +        fd = _open_pmu(type, &engines->num_counters, 
>> &engine->engine_active_ticks,
>> +                   &engines->fd);
>> +        if (fd < 0)
>> +            return -1;
>> +        fd = _open_pmu(type, &engines->num_counters, 
>> &engine->engine_total_ticks,
>> +                   &engines->fd);
>> +        if (fd < 0)
>> +            return -1;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static double pmu_active_percentage(struct xe_engine *engine)
>> +{
>> +    double pmu_active_ticks = engine->engine_active_ticks.val.cur -
>> +                  engine->engine_active_ticks.val.prev;
>> +    double pmu_total_ticks = engine->engine_total_ticks.val.cur -
>> +                 engine->engine_total_ticks.val.prev;
>> +    double percentage;
>> +
>> +    percentage = (pmu_active_ticks * 100) / pmu_total_ticks;
>> +    return percentage;
>> +}
>> +
>> +static int
>> +print_device_description(const void *obj, int lines, int w, int h)
>> +{
>> +    char *desc;
>> +    int len;
>> +
>> +    len = asprintf(&desc, "DRIVER: %s || BDF: %s",
>> +               ((struct xe_gputop *)obj)->card->driver,
>> +               ((struct xe_gputop *)obj)->card->pci_slot_name);
>> +
>> +    printf("\033[7m%s%*s\033[0m\n",
>> +           desc,
>> +           (int)(w - len), " ");
>> +    lines++;
>> +    free(desc);
>> +    return lines;
>> +}
>> +
>> +static int
>> +print_engines_header(struct xe_pmu_device *engines,
>> +             int lines, int con_w, int con_h)
>> +{
>> +    const char *a;
>> +
>> +    for (unsigned int i = 0;
>> +         i < engines->num_engines && lines < con_h;
>> +         i++) {
>> +        struct xe_engine *engine = engine_ptr(engines, i);
>> +
>> +        if (!engine->num_counters)
>> +            continue;
>> +
>> +        a = "            ENGINES   ACTIVITY  ";
>> +
>> +        printf("\033[7m%s%*s\033[0m\n",
>> +               a,
>> +               (int)(con_w - strlen(a)), " ");
>> +        lines++;
>> +
>> +        break;
>> +    }
>> +
>> +    return lines;
>> +}
>> +
>> +static int
>> +print_engine(struct xe_pmu_device *engines, unsigned int i,
>> +         int lines, int con_w, int con_h)
>> +{
>> +    struct xe_engine *engine = engine_ptr(engines, i);
>> +    double percentage = pmu_active_percentage(engine);
>> +
>> +    printf("%*s", (int)(strlen("            ENGINES")), 
>> engine->display_name);
>> +    print_percentage_bar(percentage, con_w - strlen("            
>> ENGINES"));
>> +    printf("\n");
>> +
>> +    return ++lines;
>> +}
>> +
>> +int xe_print_engines(const void *obj, int lines, int w, int h)
>> +{
>> +    struct xe_pmu_device *show = ((struct xe_gputop *)obj)->eng_obj;
>> +
>> +    lines = print_device_description(obj, lines, w, h);
>> +
>> +    lines = print_engines_header(show, lines, w,  h);
>> +
>> +    for (unsigned int i = 0; i < show->num_engines && lines < h; i++)
>> +        lines = print_engine(show, i, lines, w, h);
>> +
>> +    lines = print_engines_footer(lines, w, h);
>> +
>> +    return lines;
>> +}
>> +
>> diff --git a/tools/gputop/xe_gputop.h b/tools/gputop/xe_gputop.h
>> new file mode 100644
>> index 000000000..825ac7e34
>> --- /dev/null
>> +++ b/tools/gputop/xe_gputop.h
>> @@ -0,0 +1,62 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2025 Intel Corporation
>> + */
>> +
>> +#ifndef __XE_GPUTOP_H__
>> +#define __XE_GPUTOP_H__
>> +
>> +#include <dirent.h>
>> +
>> +#include "igt_device_scan.h"
>> +#include "igt_perf.h"
>> +#include "utils.h"
>> +#include "xe/xe_query.h"
>> +
>> +struct xe_pmu_pair {
>> +    uint64_t cur;
>> +    uint64_t prev;
>> +};
>
> Is an extra struct needed here. you can add it in below struct

Not technically but imo it helps in better readability.

Thanks,
Soham

>
>> +
>> +struct xe_pmu_counter {
>> +    uint64_t type;
>> +    uint64_t config;
>> +    unsigned int idx;
>> +    struct xe_pmu_pair val;
>> +    int fd;
>> +    bool present;
>> +};
>> +
>> +struct xe_engine {
>> +    const char *name;
>> +    char *display_name;
>> +    struct drm_xe_engine_class_instance drm_xe_engine;
>
> %s/drm_xe_engine/engine
>
>> +    unsigned int num_counters;
>> +    struct xe_pmu_counter engine_active_ticks;
>> +    struct xe_pmu_counter engine_total_ticks;
>> +};
>> +
>> +struct xe_pmu_device {
>> +    unsigned int num_engines;
>> +    unsigned int num_counters;
>> +    int fd;
>> +    char *device;
>> +    struct xe_engine engine;
>> +};
>> +
>> +struct xe_gputop {
>> +    char *pmu_device;
>> +    struct igt_device_card *card;
>> +    struct xe_pmu_device *eng_obj;
>> +};
>> +
>> +void xe_gputop_init(void *ptr,
>> +            struct igt_device_card *card);
>
> Wrap around at 100
>
> Thanks
> Riana
>
>> +void xe_populate_device_instances(struct gputop_device *dv);
>> +void *xe_populate_engines(const void *obj);
>> +void xe_pmu_sample(const void *obj);
>> +int xe_pmu_init(const void *obj);
>> +int xe_print_engines(const void *obj, int lines, int w, int h);
>> +void xe_clean_up(void *obj, int len);
>> +
>> +#endif /* __XE_GPUTOP_H__ */
>


More information about the igt-dev mailing list