From: Tvrtko Ursulin <tvrtko.ursulin@xxxxxxxxx> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@xxxxxxxxx> --- tools/intel_gpu_top.c | 360 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 351 insertions(+), 9 deletions(-) diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c index 8990ef17b771..02c5a7d77614 100644 --- a/tools/intel_gpu_top.c +++ b/tools/intel_gpu_top.c @@ -20,9 +20,6 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * - * Authors: - * Eric Anholt <eric@xxxxxxxxxx> - * Eugeni Dodonov <eugeni.dodonov@xxxxxxxxx> */ #include <stdio.h> @@ -41,6 +38,8 @@ #include <errno.h> #include <math.h> #include <locale.h> +#include <limits.h> +#include <signal.h> #include "igt_perf.h" @@ -701,8 +700,300 @@ 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" + +bool __stats_enabled; + +static int __set_stats(bool val) +{ + int fd, ret; + + fd = open(SYSFS_ENABLE, O_WRONLY); + if (fd < 0) + return -errno; + + ret = write(fd, val ? "1" : "0", 2); + if (ret < 0) + return -errno; + else if (ret < 2) + return 1; + + close(fd); + + return 0; +} + +static void __restore_stats(void) +{ + int ret; + + if (__stats_enabled) + return; + + ret = __set_stats(false); + if (ret) + fprintf(stderr, "Failed to disable per-client stats! (%d)\n", + ret); +} + +static void __restore_stats_signal(int sig) +{ + exit(0); +} + +static void enable_stats(void) +{ + int fd, ret; + + fd = open(SYSFS_ENABLE, O_RDONLY); + if (fd < 0) + return; + + close(fd); + + __stats_enabled = filename_to_u64(SYSFS_ENABLE, 10); + if (__stats_enabled) + return; + + ret = __set_stats(true); + if (!ret) { + if (atexit(__restore_stats)) + fprintf(stderr, "Failed to register exit handler!"); + + if (signal(SIGINT, __restore_stats_signal)) + fprintf(stderr, "Failed to register signal handler!"); + } else { + fprintf(stderr, "Failed to enable per-client stats! (%d)\n", + ret); + } +} + +#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 +1007,10 @@ 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(' '); + bar_len = max_len - 2 - (bar_len + 7) / 8; + if (bar_len > max_len) + bar_len = max_len; + n_spaces(bar_len); putchar('|'); } @@ -784,12 +1077,15 @@ int main(int argc, char **argv) return 1; } + enable_stats(); + /* Load average setup. */ period = (double)period_us / 1e6; for (i = 0; i < NUM_LOADS; i++) engines->load_exp[i] = exp(-period / load_period[i]); pmu_sample(engines); + scan(engines); for (;;) { double t, qd = 0; @@ -802,7 +1098,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 +1109,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 +1158,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 +1203,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 +1240,49 @@ 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 name_len = + strlen(engine->display_name); + unsigned int pad = (engine_w - name_len) / 2; + + + n_spaces(pad); + printf("%s", engine->display_name); + n_spaces(engine_w - pad - name_len); + len += pad + name_len + (engine_w - pad - + name_len); + } + 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); } -- 2.17.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx