From patchwork Wed Oct 3 12:07:18 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tvrtko Ursulin X-Patchwork-Id: 10624737 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9BE9D14BD for ; Wed, 3 Oct 2018 12:07:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8E56328942 for ; Wed, 3 Oct 2018 12:07:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 827682896A; Wed, 3 Oct 2018 12:07:37 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id DA49928942 for ; Wed, 3 Oct 2018 12:07:36 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DCDAF6E478; Wed, 3 Oct 2018 12:07:33 +0000 (UTC) X-Original-To: Intel-gfx@lists.freedesktop.org Delivered-To: Intel-gfx@lists.freedesktop.org Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com [IPv6:2a00:1450:4864:20::42f]) by gabe.freedesktop.org (Postfix) with ESMTPS id B7E2C6E473 for ; Wed, 3 Oct 2018 12:07:31 +0000 (UTC) Received: by mail-wr1-x42f.google.com with SMTP id z4-v6so5845899wrb.1 for ; Wed, 03 Oct 2018 05:07:31 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=J1Pr4c87Wv1N/yH6uyipjV5Ih5OH4atmwT6EL9hJ6T0=; b=Ths4jtp7duAUE+645Qzqa4hPXGNL3YOYaA0CH6CVRAzzCPBIqm5qiG2GAGEolKOtez yVqsCXf7EN6ahYI/Zi69v87qa8LAtFLmHA52OBkkiZAYyQ6bPn2+HFGXOnPUKnfRBwbn fJRlBzJJBxKbxL3/NaowMPa03nUqXEcKnrmkrNXqEhCv0L+Q46QzsNjEAaOQJneTjMzW EFM/iVcMtJs4VJb8cL35j/l1gIi0DKXyCJG+rTQv0UZvjze+d3JESpTT2D2PDPqcXwtq uZVGlx7oqZj2ExeseDpm4mHueHe5ZP9qARubsDKoXDmpNfE4vN3R9qWd3bsz9snolLSW iL8w== X-Gm-Message-State: ABuFfogR2lmnwyt3oyjvNj5afozlRj8ASlIfMuHZ3G2v6M48b9ZxpEUG IIqTawLx1VrfnS1lc80oHyOwpA== X-Google-Smtp-Source: ACcGV63y4E5GOOlYDvj511Dle2rgZis80krRSgoDF55BgKAElPbjGoNaT+OkIheQZB1JLKLEyfMyDA== X-Received: by 2002:adf:f1ce:: with SMTP id z14-v6mr1066245wro.214.1538568450111; Wed, 03 Oct 2018 05:07:30 -0700 (PDT) Received: from localhost.localdomain ([95.144.165.37]) by smtp.gmail.com with ESMTPSA id u191-v6sm1627707wmd.31.2018.10.03.05.07.29 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 03 Oct 2018 05:07:29 -0700 (PDT) From: Tvrtko Ursulin X-Google-Original-From: Tvrtko Ursulin To: igt-dev@lists.freedesktop.org Date: Wed, 3 Oct 2018 13:07:18 +0100 Message-Id: <20181003120718.6898-7-tvrtko.ursulin@linux.intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181003120718.6898-1-tvrtko.ursulin@linux.intel.com> References: <20181003120718.6898-1-tvrtko.ursulin@linux.intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [RFC i-g-t 6/6] intel-gpu-top: Support for client stats X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Intel-gfx@lists.freedesktop.org Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP From: Tvrtko Ursulin Signed-off-by: Tvrtko Ursulin --- 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 - * Eugeni Dodonov */ #include @@ -41,6 +38,8 @@ #include #include #include +#include +#include #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); }