diff options
author | Tvrtko Ursulin <tvrtko.ursulin@intel.com> | 2018-02-14 18:38:13 +0000 |
---|---|---|
committer | Tvrtko Ursulin <tvrtko.ursulin@intel.com> | 2018-04-17 17:58:47 +0100 |
commit | ec9292966b59ba3983a29555d04f2b51b057a682 (patch) | |
tree | a4e84ccdebdbff4617b6deea5cf75144ad7c4658 | |
parent | 12af154b33d482478ae4c1eb4233256268780e1e (diff) |
intel-gpu-top: Support for client statspmuq
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
-rw-r--r-- | tools/intel_gpu_top.c | 282 |
1 files changed, 276 insertions, 6 deletions
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index 4eb54081..1add2422 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -41,6 +41,7 @@ #include <errno.h> #include <math.h> #include <locale.h> +#include <limits.h> #include "igt_perf.h" @@ -701,8 +702,233 @@ static void pmu_sample(struct engines *engines) } } +enum client_status { + FREE = 0, /* mbz */ + ALIVE, + PROBE +}; + +struct client { + enum client_status status; + unsigned int id; + unsigned int pid; + char name[128]; + unsigned int samples; + unsigned long total; + struct engines *engines; + unsigned long *val; + uint64_t *last; +}; + +#define SYSFS_ENABLE "/sys/class/drm/card0/clients/enable_stats" +#define SYSFS_CLIENTS "/sys/class/drm/card0/clients/" + +static struct client *clients; +static unsigned int num_clients; + +#define for_each_client(c, tmp) \ + for (tmp = num_clients, c = clients; tmp > 0; tmp--, c++) + +static uint64_t read_client_busy(unsigned int id, const char *engine) +{ + char buf[256]; + ssize_t ret; + + ret = snprintf(buf, sizeof(buf), SYSFS_CLIENTS "/%u/busy/%s", + id, engine); + assert(ret > 0 && ret < sizeof(buf)); + if (ret <= 0 || ret == sizeof(buf)) + return 0; + + return filename_to_u64(buf, 10); +} + +static struct client *find_client(enum client_status status, unsigned int id) +{ + struct client *c; + unsigned int tmp; + + for_each_client(c, tmp) { + if ((status == FREE && c->status == FREE) || + (status == c->status && c->id == id)) + return c; + } + + return NULL; +} + +static void update_client(struct client *c, unsigned int pid, char *name) +{ + uint64_t val[c->engines->num_engines]; + unsigned int i; + + if (c->pid != pid) + c->pid = pid; + + if (strncmp(c->name, name, sizeof(c->name))) + strncpy(c->name, name, sizeof(c->name)); + + for (i = 0; i < c->engines->num_engines; i++) { + struct engine *engine = engine_ptr(c->engines, i); + + val[i] = read_client_busy(c->id, engine->name); + } + + c->total = 0; + + for (i = 0; i < c->engines->num_engines; i++) { + assert(val[i] >= c->last[i]); + c->val[i] = val[i] - c->last[i]; + c->total += c->val[i]; + c->last[i] = val[i]; + } + + c->samples++; + c->status = ALIVE; +} + +static void +add_client(unsigned int id, unsigned int pid, char *name, + struct engines *engines) +{ + struct client *c; + + assert(!find_client(ALIVE, id)); + + c = find_client(FREE, 0); + if (!c) { + unsigned int idx = num_clients; + + num_clients += (num_clients + 2) / 2; + clients = realloc(clients, num_clients * sizeof(*c)); + assert(clients); + c = &clients[idx]; + memset(c, 0, (num_clients - idx) * sizeof(*c)); + } + + c->id = id; + c->engines = engines; + c->val = calloc(engines->num_engines, sizeof(c->val)); + c->last = calloc(engines->num_engines, sizeof(c->last)); + assert(c->val && c->last); + + update_client(c, pid, name); +} + +static void free_client(struct client *c) +{ + free(c->val); + free(c->last); + memset(c, 0, sizeof(*c)); +} + +static char *read_client_sysfs(unsigned int id, const char *field) +{ + char buf[256]; + ssize_t ret; + + ret = snprintf(buf, sizeof(buf), SYSFS_CLIENTS "/%u/%s", id, field); + assert(ret > 0 && ret < sizeof(buf)); + if (ret <= 0 || ret == sizeof(buf)) + return NULL; + + ret = filename_to_buf(buf, buf, sizeof(buf)); + assert(ret == 0); + if (ret) + return NULL; + + return strdup(buf); +} + +static void scan(struct engines *engines) +{ + struct dirent *dent; + struct client *c; + char *pid, *name; + unsigned int tmp; + unsigned int id; + DIR *d; + + for_each_client(c, tmp) { + if (c->status == ALIVE) + c->status = PROBE; + } + + d = opendir(SYSFS_CLIENTS); + if (!d) + return; + + while ((dent = readdir(d)) != NULL) { + if (dent->d_type != DT_DIR) + continue; + if (!isdigit(dent->d_name[0])) + continue; + + id = atoi(dent->d_name); + + name = read_client_sysfs(id, "name"); + assert(name); + if (!name) + continue; + + pid = read_client_sysfs(id, "pid"); + assert(pid); + if (!pid) { + free(name); + continue; + } + + c = find_client(PROBE, id); + if (c) { + update_client(c, atoi(pid), name); + continue; + } + + add_client(id, atoi(pid), name, engines); + + free(name); + free(pid); + } + + closedir(d); + + for_each_client(c, tmp) { + if (c->status == PROBE) + free_client(c); + } +} + +static int cmp(const void *_a, const void *_b) +{ + const struct client *a = _a; + const struct client *b = _b; + long tot_a = a->total; + long tot_b = b->total; + + tot_a *= a->status == ALIVE && a->samples > 1; + tot_b *= b->status == ALIVE && b->samples > 1; + + tot_b -= tot_a; + + if (!tot_b) + return (int)b->id - a->id; + + while (tot_b > INT_MAX || tot_b < INT_MIN) + tot_b /= 2; + + return tot_b; +} + static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" }; +static void n_spaces(const unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + putchar(' '); +} + static void print_percentage_bar(double percent, int max_len) { @@ -716,8 +942,7 @@ print_percentage_bar(double percent, int max_len) if (i) printf("%s", bars[i]); - for (i = 0; i < (max_len - 2 - (bar_len + 7) / 8); i++) - putchar(' '); + n_spaces(max_len - 2 - (bar_len + 7) / 8); putchar('|'); } @@ -790,6 +1015,7 @@ int main(int argc, char **argv) engines->load_exp[i] = exp(-period / load_period[i]); pmu_sample(engines); + scan(engines); for (;;) { double t, qd = 0; @@ -802,7 +1028,8 @@ int main(int argc, char **argv) char reads[BUFSZ]; char writes[BUFSZ]; struct winsize ws; - unsigned int j; + unsigned int len, engine_w, j; + struct client *c; int lines = 0; /* Update terminal size. */ @@ -812,9 +1039,10 @@ int main(int argc, char **argv) } pmu_sample(engines); - t = (double)(engines->ts.cur - engines->ts.prev) / 1e9; + scan(engines); + qsort(clients, num_clients, sizeof(*c), cmp); - printf("\033[H\033[J"); + t = (double)(engines->ts.cur - engines->ts.prev) / 1e9; pmu_calc(&engines->freq_req, freq, BUFSZ, 4, 0, 1.0, t, 1); pmu_calc(&engines->freq_act, fact, BUFSZ, 4, 0, 1.0, t, 1); @@ -860,6 +1088,8 @@ int main(int argc, char **argv) qd); } + printf("\033[H\033[J"); + if (lines++ < con_h) printf("intel-gpu-top - load avg %5.2f, %5.2f, %5.2f; %s/%s MHz; %s%% RC6; %s %s; %s irqs/s\n", engines->load_avg[0], @@ -903,7 +1133,6 @@ int main(int argc, char **argv) struct engine *engine = engine_ptr(engines, i); unsigned int max_w = con_w - 1; char qdbuf[NUM_LOADS][BUFSZ]; - unsigned int len; char sema[BUFSZ]; char wait[BUFSZ]; char busy[BUFSZ]; @@ -941,6 +1170,47 @@ int main(int argc, char **argv) if (lines++ < con_h) printf("\n"); + if (lines++ < con_h) { + printf("\033[7m"); + len = printf("%5s%16s", "PID", "NAME"); + + engine_w = (con_w - len) / engines->num_engines; + for (i = 0; i < engines->num_engines; i++) { + struct engine *engine = engine_ptr(engines, i); + unsigned int pad = + (engine_w - strlen(engine->name)) / 2; + + n_spaces(pad); + printf("%s", engine->name); + n_spaces(engine_w - pad - strlen(engine->name)); + len += pad + strlen(engine->name) + + (engine_w - pad - strlen(engine->name)); + } + n_spaces(con_w - len); + printf("\033[0m\n"); + } + + for_each_client(c, i) { + if (lines++ > con_h) + break; + + assert(c->status != PROBE); + if (c->status != ALIVE || c->samples < 2) + break; + + printf("%5u%16s ", c->pid, c->name); + + for (j = 0; j < engines->num_engines; j++) { + double pct; + + pct = (double)c->val[j] / period_us / 1e3 * 100; + + print_percentage_bar(pct, engine_w); + } + + putchar('\n'); + } + usleep(period_us); } |