* [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
@ 2019-02-11 11:45 Tvrtko Ursulin
2019-02-11 11:45 ` [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability Tvrtko Ursulin
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Tvrtko Ursulin @ 2019-02-11 11:45 UTC (permalink / raw)
To: igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi, Tvrtko Ursulin
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Two new output modes are added: listing of text data to standard out (-l
on the command line), and dumping of JSON formatted records (-J), also to
standard out.
The first mode is selected automatically when non-interactive standard out
is detected.
Example of text output:
Freq MHz IRQ RC6 Power IMC MiB/s RCS/0 BCS/0 VCS/0 VCS/1 VECS/0
req act /s % W rd wr % se wa % se wa % se wa % se wa % se wa
0 0 0 0 0.00 360 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 35 2 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 34 2 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 143 6 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 169 7 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 169 7 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
Example of JSON output:
{
"period": {
"duration": 1002.525224,
"unit": "ms"
},
"frequency": {
"requested": 349.118398,
"actual": 349.118398,
"unit": "MHz"
},
"interrupts": {
"count": 0.000000,
"unit": "irq/s"
},
"rc6": {
"value": 99.897752,
"unit": "%"
},
"power": {
"value": 0.000000,
"unit": "W"
},
"imc-bandwidth": {
"reads": 149.683843,
"writes": 6.104093,
"unit": "MiB/s"
},
"engines": {
"Render/3D/0": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
},
"Blitter/0": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
},
"Video/0": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
},
"Video/1": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
},
"VideoEnhance/0": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
}
}
}
v2:
* Show example output in commit message.
* Install signal handler to complete output on SIGINT. (Chris Wilson)
v3:
* Use asprintf where possible. (Chris Wilson)
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
References: https://bugs.freedesktop.org/show_bug.cgi?id=108689
Cc: Eero Tamminen <eero.t.tamminen@intel.com>
Cc: 3.14pi@ukr.net
Cc: Chris Wilson <chris@chris-wilson.co.uk>
---
man/intel_gpu_top.rst | 9 +-
tools/intel_gpu_top.c | 761 +++++++++++++++++++++++++++++++++++-------
2 files changed, 644 insertions(+), 126 deletions(-)
diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index 19c712307d28..d5bda093c8e8 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -7,9 +7,9 @@ Display a top-like summary of Intel GPU usage
---------------------------------------------
.. include:: defs.rst
:Author: IGT Developers <igt-dev@lists.freedesktop.org>
-:Date: 2018-04-04
+:Date: 2019-02-08
:Version: |PACKAGE_STRING|
-:Copyright: 2009,2011,2012,2016,2018 Intel Corporation
+:Copyright: 2009,2011,2012,2016,2018,2019 Intel Corporation
:Manual section: |MANUAL_SECTION|
:Manual group: |MANUAL_GROUP|
@@ -31,6 +31,11 @@ OPTIONS
-s <ms>
Refresh period in milliseconds.
+-l
+ List text data to standard out.
+
+-J
+ Output JSON formatted data to standard output.
-h
Show help text.
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index b923c3cfbe97..900979eea7a1 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -1,5 +1,5 @@
/*
- * Copyright © 2007-2018 Intel Corporation
+ * Copyright © 2007-2019 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -19,10 +19,6 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Eric Anholt <eric@anholt.net>
- * Eugeni Dodonov <eugeni.dodonov@intel.com>
*/
#include <stdio.h>
@@ -41,6 +37,8 @@
#include <errno.h>
#include <math.h>
#include <locale.h>
+#include <limits.h>
+#include <signal.h>
#include "igt_perf.h"
@@ -58,7 +56,8 @@ struct pmu_counter {
struct engine {
const char *name;
- const char *display_name;
+ char *display_name;
+ char *short_name;
unsigned int class;
unsigned int instance;
@@ -142,6 +141,22 @@ static const char *class_display_name(unsigned int class)
}
}
+static const char *class_short_name(unsigned int class)
+{
+ switch (class) {
+ case I915_ENGINE_CLASS_RENDER:
+ return "RCS";
+ case I915_ENGINE_CLASS_COPY:
+ return "BCS";
+ case I915_ENGINE_CLASS_VIDEO:
+ return "VCS";
+ case I915_ENGINE_CLASS_VIDEO_ENHANCE:
+ return "VECS";
+ default:
+ return "UNKN";
+ }
+}
+
static int engine_cmp(const void *__a, const void *__b)
{
const struct engine *a = (struct engine *)__a;
@@ -220,17 +235,18 @@ static struct engines *discover_engines(void)
I915_PMU_SAMPLE_BITS) &
((1 << I915_PMU_SAMPLE_INSTANCE_BITS) - 1);
- ret = snprintf(buf, sizeof(buf), "%s/%u",
+ ret = asprintf(&engine->display_name, "%s/%u",
class_display_name(engine->class),
engine->instance);
- if (ret < 0 || ret == sizeof(buf)) {
- ret = ENOBUFS;
+ if (!engine->display_name) {
+ ret = errno;
break;
}
- ret = 0;
- engine->display_name = strdup(buf);
- if (!engine->display_name) {
+ ret = asprintf(&engine->short_name, "%s/%u",
+ class_short_name(engine->class),
+ engine->instance);
+ if (!engine->short_name) {
ret = errno;
break;
}
@@ -242,6 +258,8 @@ static struct engines *discover_engines(void)
ret = errno;
break;
}
+
+ ret = 0;
}
if (ret) {
@@ -551,7 +569,7 @@ static uint64_t pmu_read_multi(int fd, unsigned int num, uint64_t *val)
return buf[1];
}
-static double __pmu_calc(struct pmu_pair *p, double d, double t, double s)
+static double pmu_calc(struct pmu_pair *p, double d, double t, double s)
{
double v;
@@ -576,30 +594,6 @@ static void fill_str(char *buf, unsigned int bufsz, char c, unsigned int num)
*buf = 0;
}
-static void pmu_calc(struct pmu_counter *cnt,
- char *buf, unsigned int bufsz,
- unsigned int width, unsigned width_dec,
- double d, double t, double s)
-{
- double val;
- int len;
-
- assert(bufsz >= (width + width_dec + 1));
-
- if (!cnt->present) {
- fill_str(buf, bufsz, '-', width + width_dec);
- return;
- }
-
- val = __pmu_calc(&cnt->val, d, t, s);
-
- len = snprintf(buf, bufsz, "%*.*f", width + width_dec, width_dec, val);
- if (len < 0 || len == bufsz) {
- fill_str(buf, bufsz, 'X', width + width_dec);
- return;
- }
-}
-
static uint64_t __pmu_read_single(int fd, uint64_t *ts)
{
uint64_t data[2] = { };
@@ -697,11 +691,559 @@ usage(const char *appname)
"\n"
"\tThe following parameters are optional:\n\n"
"\t[-s <ms>] Refresh period in milliseconds (default %ums).\n"
+ "\t[-l] List data to standard out.\n"
+ "\t[-J] JSON data to standard out.\n"
"\t[-h] Show this help text.\n"
"\n",
appname, DEFAULT_PERIOD_MS);
}
+static enum {
+ INTERACTIVE,
+ STDOUT,
+ JSON
+} output_mode;
+
+struct cnt_item {
+ struct pmu_counter *pmu;
+ unsigned int fmt_d;
+ unsigned int fmt_dd;
+ double d;
+ double t;
+ double s;
+ const char *name;
+ const char *unit;
+
+ /* Internal fields. */
+ char buf[16];
+};
+
+struct cnt_group {
+ const char *name;
+ const char *display_name;
+ struct cnt_item *items;
+};
+
+static unsigned int json_indent_level;
+
+static const char *json_indent[] = {
+ "",
+ "\t",
+ "\t\t",
+ "\t\t\t",
+ "\t\t\t\t",
+ "\t\t\t\t\t",
+};
+
+#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
+
+static unsigned int json_prev_struct_members;
+static unsigned int json_struct_members;
+
+static void
+json_open_struct(const char *name)
+{
+ assert(json_indent_level < ARRAY_SIZE(json_indent));
+
+ json_prev_struct_members = json_struct_members;
+ json_struct_members = 0;
+
+ if (name)
+ printf("%s%s\"%s\": {\n",
+ json_prev_struct_members ? ",\n" : "",
+ json_indent[json_indent_level],
+ name);
+ else
+ printf("%s\n%s{\n",
+ json_prev_struct_members ? "," : "",
+ json_indent[json_indent_level]);
+
+ json_indent_level++;
+}
+
+static void
+json_close_struct(void)
+{
+ assert(json_indent_level > 0);
+
+ printf("\n%s}", json_indent[--json_indent_level]);
+
+ if (json_indent_level == 0)
+ fflush(stdout);
+}
+
+static unsigned int
+json_add_member(const struct cnt_group *parent, struct cnt_item *item,
+ unsigned int headers)
+{
+ assert(json_indent_level < ARRAY_SIZE(json_indent));
+
+ printf("%s%s\"%s\": ",
+ json_struct_members ? ",\n" : "",
+ json_indent[json_indent_level], item->name);
+
+ json_struct_members++;
+
+ if (!strcmp(item->name, "unit"))
+ printf("\"%s\"", item->unit);
+ else
+ printf("%f",
+ pmu_calc(&item->pmu->val, item->d, item->t, item->s));
+
+ return 1;
+}
+
+static unsigned int stdout_level;
+
+#define STDOUT_HEADER_REPEAT 20
+static unsigned int stdout_lines = STDOUT_HEADER_REPEAT;
+
+static void
+stdout_open_struct(const char *name)
+{
+ stdout_level++;
+ assert(stdout_level > 0);
+}
+
+static void
+stdout_close_struct(void)
+{
+ assert(stdout_level > 0);
+ if (--stdout_level == 0) {
+ stdout_lines++;
+ printf("\n");
+ fflush(stdout);
+ }
+}
+
+static unsigned int
+stdout_add_member(const struct cnt_group *parent, struct cnt_item *item,
+ unsigned int headers)
+{
+ unsigned int fmt_tot = item->fmt_d + (item->fmt_dd ? 1 : 0);
+ char buf[fmt_tot + 1];
+ double val;
+ int len;
+
+ if (!item->pmu)
+ return 0;
+ else if (!item->pmu->present)
+ return 0;
+
+ if (headers == 1) {
+ unsigned int grp_tot = 0;
+ struct cnt_item *it;
+
+ if (item != parent->items)
+ return 0;
+
+ for (it = parent->items; it->pmu; it++) {
+ if (!it->pmu->present)
+ continue;
+
+ grp_tot += 1 + it->fmt_d + (it->fmt_dd ? 1 : 0);
+ }
+
+ printf("%*s ", grp_tot - 1, parent->display_name);
+ return 0;
+ } else if (headers == 2) {
+ printf("%*s ", fmt_tot, item->unit ?: item->name);
+ return 0;
+ }
+
+ val = pmu_calc(&item->pmu->val, item->d, item->t, item->s);
+
+ len = snprintf(buf, sizeof(buf), "%*.*f", fmt_tot, item->fmt_dd, val);
+ if (len < 0 || len == sizeof(buf))
+ fill_str(buf, sizeof(buf), 'X', fmt_tot);
+
+ len = printf("%s ", buf);
+
+ return len > 0 ? len : 0;
+}
+
+static void
+term_open_struct(const char *name)
+{
+}
+
+static void
+term_close_struct(void)
+{
+}
+
+static unsigned int
+term_add_member(const struct cnt_group *parent, struct cnt_item *item,
+ unsigned int headers)
+{
+ unsigned int fmt_tot = item->fmt_d + (item->fmt_dd ? 1 : 0);
+ double val;
+ int len;
+
+ if (!item->pmu)
+ return 0;
+
+ assert(fmt_tot <= sizeof(item->buf));
+
+ if (!item->pmu->present) {
+ fill_str(item->buf, sizeof(item->buf), '-', fmt_tot);
+ return 1;
+ }
+
+ val = pmu_calc(&item->pmu->val, item->d, item->t, item->s);
+ len = snprintf(item->buf, sizeof(item->buf),
+ "%*.*f",
+ fmt_tot, item->fmt_dd, val);
+
+ if (len < 0 || len == sizeof(item->buf))
+ fill_str(item->buf, sizeof(item->buf), 'X', fmt_tot);
+
+ return 1;
+}
+
+struct print_operations {
+ void (*open_struct)(const char *name);
+ void (*close_struct)(void);
+ unsigned int (*add_member)(const struct cnt_group *parent,
+ struct cnt_item *item,
+ unsigned int headers);
+ bool (*print_group)(struct cnt_group *group, unsigned int headers);
+};
+
+static const struct print_operations *pops;
+
+static unsigned int
+present_in_group(const struct cnt_group *grp)
+{
+ unsigned int present = 0;
+ struct cnt_item *item;
+
+ for (item = grp->items; item->name; item++) {
+ if (item->pmu && item->pmu->present)
+ present++;
+ }
+
+ return present;
+}
+
+static bool
+print_group(struct cnt_group *grp, unsigned int headers)
+{
+ unsigned int consumed = 0;
+ struct cnt_item *item;
+
+ if (!present_in_group(grp))
+ return false;
+
+ pops->open_struct(grp->name);
+
+ for (item = grp->items; item->name; item++)
+ consumed += pops->add_member(grp, item, headers);
+
+ pops->close_struct();
+
+ return consumed;
+}
+
+static bool
+term_print_group(struct cnt_group *grp, unsigned int headers)
+{
+ unsigned int consumed = 0;
+ struct cnt_item *item;
+
+ pops->open_struct(grp->name);
+
+ for (item = grp->items; item->name; item++)
+ consumed += pops->add_member(grp, item, headers);
+
+ pops->close_struct();
+
+ return consumed;
+}
+
+static const struct print_operations json_pops = {
+ .open_struct = json_open_struct,
+ .close_struct = json_close_struct,
+ .add_member = json_add_member,
+ .print_group = print_group,
+};
+
+static const struct print_operations stdout_pops = {
+ .open_struct = stdout_open_struct,
+ .close_struct = stdout_close_struct,
+ .add_member = stdout_add_member,
+ .print_group = print_group,
+};
+
+static const struct print_operations term_pops = {
+ .open_struct = term_open_struct,
+ .close_struct = term_close_struct,
+ .add_member = term_add_member,
+ .print_group = term_print_group,
+};
+
+static bool print_groups(struct cnt_group **groups)
+{
+ unsigned int headers = stdout_lines % STDOUT_HEADER_REPEAT + 1;
+ bool print_data = true;
+
+ if (output_mode == STDOUT && (headers == 1 || headers == 2)) {
+ for (struct cnt_group **grp = groups; *grp; grp++)
+ print_data = pops->print_group(*grp, headers);
+ }
+
+ for (struct cnt_group **grp = groups; print_data && *grp; grp++)
+ pops->print_group(*grp, false);
+
+ return print_data;
+}
+
+static int
+print_header(struct engines *engines, double t,
+ int lines, int con_w, int con_h, bool *consumed)
+{
+ struct pmu_counter fake_pmu = {
+ .present = true,
+ .val.cur = 1,
+ };
+ struct cnt_item period_items[] = {
+ { &fake_pmu, 0, 0, 1.0, 1.0, t * 1e3, "duration" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "ms" },
+ { },
+ };
+ struct cnt_group period_group = {
+ .name = "period",
+ .items = period_items,
+ };
+ struct cnt_item freq_items[] = {
+ { &engines->freq_req, 4, 0, 1.0, t, 1, "requested", "req" },
+ { &engines->freq_act, 4, 0, 1.0, t, 1, "actual", "act" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "MHz" },
+ { },
+ };
+ struct cnt_group freq_group = {
+ .name = "frequency",
+ .display_name = "Freq MHz",
+ .items = freq_items,
+ };
+ struct cnt_item irq_items[] = {
+ { &engines->irq, 8, 0, 1.0, t, 1, "count", "/s" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "irq/s" },
+ { },
+ };
+ struct cnt_group irq_group = {
+ .name = "interrupts",
+ .display_name = "IRQ",
+ .items = irq_items,
+ };
+ struct cnt_item rc6_items[] = {
+ { &engines->rc6, 3, 0, 1e9, t, 100, "value", "%" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "%" },
+ { },
+ };
+ struct cnt_group rc6_group = {
+ .name = "rc6",
+ .display_name = "RC6",
+ .items = rc6_items,
+ };
+ struct cnt_item power_items[] = {
+ { &engines->rapl, 4, 2, 1.0, t, engines->rapl_scale, "value",
+ "W" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "W" },
+ { },
+ };
+ struct cnt_group power_group = {
+ .name = "power",
+ .display_name = "Power",
+ .items = power_items,
+ };
+ struct cnt_group *groups[] = {
+ &period_group,
+ &freq_group,
+ &irq_group,
+ &rc6_group,
+ &power_group,
+ NULL
+ };
+
+ if (output_mode != JSON)
+ memmove(&groups[0], &groups[1],
+ sizeof(groups) - sizeof(groups[0]));
+
+ pops->open_struct(NULL);
+
+ *consumed = print_groups(groups);
+
+ if (output_mode == INTERACTIVE) {
+ printf("\033[H\033[J");
+
+ if (lines++ < con_h)
+ printf("intel-gpu-top - %s/%s MHz; %s%% RC6; %s %s; %s irqs/s\n",
+ freq_items[1].buf, freq_items[0].buf,
+ rc6_items[0].buf, power_items[0].buf,
+ engines->rapl_unit,
+ irq_items[0].buf);
+
+ if (lines++ < con_h)
+ printf("\n");
+ }
+
+ return lines;
+}
+
+static int
+print_imc(struct engines *engines, double t, int lines, int con_w, int con_h)
+{
+ struct cnt_item imc_items[] = {
+ { &engines->imc_reads, 6, 0, 1.0, t, engines->imc_reads_scale,
+ "reads", "rd" },
+ { &engines->imc_writes, 6, 0, 1.0, t, engines->imc_writes_scale,
+ "writes", "wr" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit" },
+ { },
+ };
+ struct cnt_group imc_group = {
+ .name = "imc-bandwidth",
+ .items = imc_items,
+ };
+ struct cnt_group *groups[] = {
+ &imc_group,
+ NULL
+ };
+ int ret;
+
+ ret = asprintf((char **)&imc_group.display_name, "IMC %s/s",
+ engines->imc_reads_unit);
+ assert(ret >= 0);
+
+ ret = asprintf((char **)&imc_items[2].unit, "%s/s",
+ engines->imc_reads_unit);
+ assert(ret >= 0);
+
+ print_groups(groups);
+
+ free((void *)imc_group.display_name);
+ free((void *)imc_items[2].unit);
+
+ if (output_mode == INTERACTIVE) {
+ if (lines++ < con_h)
+ printf(" IMC reads: %s %s/s\n",
+ imc_items[0].buf, engines->imc_reads_unit);
+
+ if (lines++ < con_h)
+ printf(" IMC writes: %s %s/s\n",
+ imc_items[1].buf, engines->imc_writes_unit);
+
+ if (lines++ < con_h)
+ printf("\n");
+ }
+
+ return lines;
+}
+
+static int
+print_engines_header(struct engines *engines, double t,
+ int lines, int con_w, int con_h)
+{
+ for (unsigned int i = 0;
+ i < engines->num_engines && lines < con_h;
+ i++) {
+ struct engine *engine = engine_ptr(engines, i);
+
+ if (!engine->num_counters)
+ continue;
+
+ pops->open_struct("engines");
+
+ if (output_mode == INTERACTIVE) {
+ const char *a = " ENGINE BUSY ";
+ const char *b = " MI_SEMA MI_WAIT";
+
+ printf("\033[7m%s%*s%s\033[0m\n",
+ a, (int)(con_w - 1 - strlen(a) - strlen(b)),
+ " ", b);
+
+ lines++;
+ }
+
+ break;
+ }
+
+ return lines;
+}
+
+static int
+print_engine(struct engines *engines, unsigned int i, double t,
+ int lines, int con_w, int con_h)
+{
+ struct engine *engine = engine_ptr(engines, i);
+ struct cnt_item engine_items[] = {
+ { &engine->busy, 6, 2, 1e9, t, 100, "busy", "%" },
+ { &engine->sema, 3, 0, 1e9, t, 100, "sema", "se" },
+ { &engine->wait, 3, 0, 1e9, t, 100, "wait", "wa" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "%" },
+ { },
+ };
+ struct cnt_group engine_group = {
+ .name = engine->display_name,
+ .display_name = engine->short_name,
+ .items = engine_items,
+ };
+ struct cnt_group *groups[] = {
+ &engine_group,
+ NULL
+ };
+
+ if (!engine->num_counters)
+ return lines;
+
+ print_groups(groups);
+
+ if (output_mode == INTERACTIVE) {
+ unsigned int max_w = con_w - 1;
+ unsigned int len;
+ char buf[128];
+ double val;
+
+ len = snprintf(buf, sizeof(buf), " %s%% %s%%",
+ engine_items[1].buf, engine_items[2].buf);
+
+ len += printf("%16s %s%% ",
+ engine->display_name, engine_items[0].buf);
+
+ val = pmu_calc(&engine->busy.val, 1e9, t, 100);
+ print_percentage_bar(val, max_w - len);
+
+ printf("%s\n", buf);
+
+ lines++;
+ }
+
+ return lines;
+}
+
+static int
+print_engines_footer(struct engines *engines, double t,
+ int lines, int con_w, int con_h)
+{
+ pops->close_struct();
+ pops->close_struct();
+
+ if (output_mode == INTERACTIVE) {
+ if (lines++ < con_h)
+ printf("\n");
+ }
+
+ return lines;
+}
+
+static bool stop_top;
+
+static void sigint_handler(int sig)
+{
+ stop_top = true;
+}
+
int main(int argc, char **argv)
{
unsigned int period_us = DEFAULT_PERIOD_MS * 1000;
@@ -711,11 +1253,17 @@ int main(int argc, char **argv)
int ret, ch;
/* Parse options */
- while ((ch = getopt(argc, argv, "s:h")) != -1) {
+ while ((ch = getopt(argc, argv, "s:Jlh")) != -1) {
switch (ch) {
case 's':
period_us = atoi(optarg) * 1000;
break;
+ case 'J':
+ output_mode = JSON;
+ break;
+ case 'l':
+ output_mode = STDOUT;
+ break;
case 'h':
usage(argv[0]);
exit(0);
@@ -726,6 +1274,31 @@ int main(int argc, char **argv)
}
}
+ if (output_mode == INTERACTIVE && isatty(1) != 1)
+ output_mode = STDOUT;
+
+ if (output_mode != INTERACTIVE) {
+ sighandler_t sig = signal(SIGINT, sigint_handler);
+
+ if (sig == SIG_ERR)
+ fprintf(stderr, "Failed to install signal handler!\n");
+ }
+
+ switch (output_mode) {
+ case INTERACTIVE:
+ pops = &term_pops;
+ break;
+ case STDOUT:
+ pops = &stdout_pops;
+ break;
+ case JSON:
+ pops = &json_pops;
+ break;
+ default:
+ assert(0);
+ break;
+ };
+
engines = discover_engines();
if (!engines) {
fprintf(stderr,
@@ -743,21 +1316,16 @@ int main(int argc, char **argv)
pmu_sample(engines);
- for (;;) {
- double t;
-#define BUFSZ 16
- char freq[BUFSZ];
- char fact[BUFSZ];
- char irq[BUFSZ];
- char rc6[BUFSZ];
- char power[BUFSZ];
- char reads[BUFSZ];
- char writes[BUFSZ];
- struct winsize ws;
+ while (!stop_top) {
+ bool consumed = false;
int lines = 0;
+ struct winsize ws;
+ double t;
/* Update terminal size. */
- if (ioctl(0, TIOCGWINSZ, &ws) != -1) {
+ if (output_mode != INTERACTIVE) {
+ con_w = con_h = INT_MAX;
+ } else if (ioctl(0, TIOCGWINSZ, &ws) != -1) {
con_w = ws.ws_col;
con_h = ws.ws_row;
}
@@ -765,87 +1333,32 @@ int main(int argc, char **argv)
pmu_sample(engines);
t = (double)(engines->ts.cur - engines->ts.prev) / 1e9;
- printf("\033[H\033[J");
-
- 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);
- pmu_calc(&engines->irq, irq, BUFSZ, 8, 0, 1.0, t, 1);
- pmu_calc(&engines->rc6, rc6, BUFSZ, 3, 0, 1e9, t, 100);
- pmu_calc(&engines->rapl, power, BUFSZ, 4, 2, 1.0, t,
- engines->rapl_scale);
- pmu_calc(&engines->imc_reads, reads, BUFSZ, 6, 0, 1.0, t,
- engines->imc_reads_scale);
- pmu_calc(&engines->imc_writes, writes, BUFSZ, 6, 0, 1.0, t,
- engines->imc_writes_scale);
-
- if (lines++ < con_h)
- printf("intel-gpu-top - %s/%s MHz; %s%% RC6; %s %s; %s irqs/s\n",
- fact, freq, rc6, power, engines->rapl_unit, irq);
-
- if (lines++ < con_h)
- printf("\n");
-
- if (engines->imc_fd) {
- if (lines++ < con_h)
- printf(" IMC reads: %s %s/s\n",
- reads, engines->imc_reads_unit);
-
- if (lines++ < con_h)
- printf(" IMC writes: %s %s/s\n",
- writes, engines->imc_writes_unit);
-
- if (++lines < con_h)
- printf("\n");
- }
-
- for (i = 0; i < engines->num_engines; i++) {
- struct engine *engine = engine_ptr(engines, i);
-
- if (engine->num_counters && lines < con_h) {
- const char *a = " ENGINE BUSY ";
- const char *b = " MI_SEMA MI_WAIT";
-
- printf("\033[7m%s%*s%s\033[0m\n",
- a,
- (int)(con_w - 1 - strlen(a) - strlen(b)),
- " ", b);
- lines++;
- break;
- }
- }
-
- for (i = 0; i < engines->num_engines && lines < con_h; i++) {
- struct engine *engine = engine_ptr(engines, i);
- unsigned int max_w = con_w - 1;
- unsigned int len;
- char sema[BUFSZ];
- char wait[BUFSZ];
- char busy[BUFSZ];
- char buf[128];
- double val;
-
- if (!engine->num_counters)
- continue;
+ if (stop_top)
+ break;
- pmu_calc(&engine->sema, sema, BUFSZ, 3, 0, 1e9, t, 100);
- pmu_calc(&engine->wait, wait, BUFSZ, 3, 0, 1e9, t, 100);
- len = snprintf(buf, sizeof(buf), " %s%% %s%%",
- sema, wait);
+ while (!consumed) {
+ lines = print_header(engines, t, lines, con_w, con_h,
+ &consumed);
- pmu_calc(&engine->busy, busy, BUFSZ, 6, 2, 1e9, t,
- 100);
- len += printf("%16s %s%% ", engine->display_name, busy);
+ if (engines->imc_fd)
+ lines = print_imc(engines, t, lines, con_w,
+ con_h);
- val = __pmu_calc(&engine->busy.val, 1e9, t, 100);
- print_percentage_bar(val, max_w - len);
+ lines = print_engines_header(engines, t, lines, con_w,
+ con_h);
- printf("%s\n", buf);
+ for (i = 0;
+ i < engines->num_engines && lines < con_h;
+ i++)
+ lines = print_engine(engines, i, t, lines,
+ con_w, con_h);
- lines++;
+ lines = print_engines_footer(engines, t, lines, con_w,
+ con_h);
}
- if (lines++ < con_h)
- printf("\n");
+ if (stop_top)
+ break;
usleep(period_us);
}
--
2.19.1
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply related [flat|nested] 8+ messages in thread* [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability
2019-02-11 11:45 [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging Tvrtko Ursulin
@ 2019-02-11 11:45 ` Tvrtko Ursulin
2019-02-11 12:12 ` [igt-dev] ✓ Fi.CI.BAT: success for series starting with [1/2] tools/intel_gpu_top: Add support for stdout logging Patchwork
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Tvrtko Ursulin @ 2019-02-11 11:45 UTC (permalink / raw)
To: igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi, Tvrtko Ursulin
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
A new -o command switch enables logging to a file.
v2:
* Support "-o -" for explicit stdout selection. (Chris Wilson)
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
References: https://bugs.freedesktop.org/show_bug.cgi?id=108689
Cc: Eero Tamminen <eero.t.tamminen@intel.com>
Cc: 3.14pi@ukr.net
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
---
man/intel_gpu_top.rst | 19 ++++++++-----
tools/intel_gpu_top.c | 63 ++++++++++++++++++++++++++++---------------
2 files changed, 53 insertions(+), 29 deletions(-)
diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index d5bda093c8e8..d487baca0c63 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -28,16 +28,21 @@ The tool gathers data using perf performance counters (PMU) exposed by i915 and
OPTIONS
=======
--s <ms>
- Refresh period in milliseconds.
+-h
+ Show help text.
+
+-J
+ Output JSON formatted data.
-l
- List text data to standard out.
+ List plain text data.
--J
- Output JSON formatted data to standard output.
--h
- Show help text.
+-o <file path | ->
+ Output to the specified file instead of standard output.
+ '-' can also be specified to explicitly select standard output.
+
+-s <ms>
+ Refresh period in milliseconds.
LIMITATIONS
===========
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 900979eea7a1..60505a606bfc 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -690,10 +690,11 @@ usage(const char *appname)
"Usage: %s [parameters]\n"
"\n"
"\tThe following parameters are optional:\n\n"
- "\t[-s <ms>] Refresh period in milliseconds (default %ums).\n"
- "\t[-l] List data to standard out.\n"
- "\t[-J] JSON data to standard out.\n"
"\t[-h] Show this help text.\n"
+ "\t[-J] Output JSON formatted data.\n"
+ "\t[-l] List plain text data.\n"
+ "\t[-o <file|->] Output to specified file or '-' for standard out.\n"
+ "\t[-s <ms>] Refresh period in milliseconds (default %ums).\n"
"\n",
appname, DEFAULT_PERIOD_MS);
}
@@ -740,6 +741,8 @@ static const char *json_indent[] = {
static unsigned int json_prev_struct_members;
static unsigned int json_struct_members;
+FILE *out;
+
static void
json_open_struct(const char *name)
{
@@ -749,14 +752,14 @@ json_open_struct(const char *name)
json_struct_members = 0;
if (name)
- printf("%s%s\"%s\": {\n",
- json_prev_struct_members ? ",\n" : "",
- json_indent[json_indent_level],
- name);
+ fprintf(out, "%s%s\"%s\": {\n",
+ json_prev_struct_members ? ",\n" : "",
+ json_indent[json_indent_level],
+ name);
else
- printf("%s\n%s{\n",
- json_prev_struct_members ? "," : "",
- json_indent[json_indent_level]);
+ fprintf(out, "%s\n%s{\n",
+ json_prev_struct_members ? "," : "",
+ json_indent[json_indent_level]);
json_indent_level++;
}
@@ -766,7 +769,7 @@ json_close_struct(void)
{
assert(json_indent_level > 0);
- printf("\n%s}", json_indent[--json_indent_level]);
+ fprintf(out, "\n%s}", json_indent[--json_indent_level]);
if (json_indent_level == 0)
fflush(stdout);
@@ -778,17 +781,17 @@ json_add_member(const struct cnt_group *parent, struct cnt_item *item,
{
assert(json_indent_level < ARRAY_SIZE(json_indent));
- printf("%s%s\"%s\": ",
+ fprintf(out, "%s%s\"%s\": ",
json_struct_members ? ",\n" : "",
json_indent[json_indent_level], item->name);
json_struct_members++;
if (!strcmp(item->name, "unit"))
- printf("\"%s\"", item->unit);
+ fprintf(out, "\"%s\"", item->unit);
else
- printf("%f",
- pmu_calc(&item->pmu->val, item->d, item->t, item->s));
+ fprintf(out, "%f",
+ pmu_calc(&item->pmu->val, item->d, item->t, item->s));
return 1;
}
@@ -811,8 +814,8 @@ stdout_close_struct(void)
assert(stdout_level > 0);
if (--stdout_level == 0) {
stdout_lines++;
- printf("\n");
- fflush(stdout);
+ fputs("\n", out);
+ fflush(out);
}
}
@@ -844,10 +847,10 @@ stdout_add_member(const struct cnt_group *parent, struct cnt_item *item,
grp_tot += 1 + it->fmt_d + (it->fmt_dd ? 1 : 0);
}
- printf("%*s ", grp_tot - 1, parent->display_name);
+ fprintf(out, "%*s ", grp_tot - 1, parent->display_name);
return 0;
} else if (headers == 2) {
- printf("%*s ", fmt_tot, item->unit ?: item->name);
+ fprintf(out, "%*s ", fmt_tot, item->unit ?: item->name);
return 0;
}
@@ -857,7 +860,7 @@ stdout_add_member(const struct cnt_group *parent, struct cnt_item *item,
if (len < 0 || len == sizeof(buf))
fill_str(buf, sizeof(buf), 'X', fmt_tot);
- len = printf("%s ", buf);
+ len = fprintf(out, "%s ", buf);
return len > 0 ? len : 0;
}
@@ -1248,13 +1251,17 @@ int main(int argc, char **argv)
{
unsigned int period_us = DEFAULT_PERIOD_MS * 1000;
int con_w = -1, con_h = -1;
+ char *output_path = NULL;
struct engines *engines;
unsigned int i;
int ret, ch;
/* Parse options */
- while ((ch = getopt(argc, argv, "s:Jlh")) != -1) {
+ while ((ch = getopt(argc, argv, "o:s:Jlh")) != -1) {
switch (ch) {
+ case 'o':
+ output_path = optarg;
+ break;
case 's':
period_us = atoi(optarg) * 1000;
break;
@@ -1274,9 +1281,21 @@ int main(int argc, char **argv)
}
}
- if (output_mode == INTERACTIVE && isatty(1) != 1)
+ if (output_mode == INTERACTIVE && (output_path || isatty(1) != 1))
output_mode = STDOUT;
+ if (output_path && strcmp(output_path, "-")) {
+ out = fopen(output_path, "w");
+
+ if (!out) {
+ fprintf(stderr, "Failed to open output file - '%s'!\n",
+ strerror(errno));
+ exit(1);
+ }
+ } else {
+ out = stdout;
+ }
+
if (output_mode != INTERACTIVE) {
sighandler_t sig = signal(SIGINT, sigint_handler);
--
2.19.1
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply related [flat|nested] 8+ messages in thread* [igt-dev] ✓ Fi.CI.BAT: success for series starting with [1/2] tools/intel_gpu_top: Add support for stdout logging
2019-02-11 11:45 [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging Tvrtko Ursulin
2019-02-11 11:45 ` [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability Tvrtko Ursulin
@ 2019-02-11 12:12 ` Patchwork
2019-02-11 12:21 ` [igt-dev] [IGT 1/2] " Chris Wilson
2019-02-11 14:09 ` [igt-dev] ✓ Fi.CI.IGT: success for series starting with [1/2] " Patchwork
3 siblings, 0 replies; 8+ messages in thread
From: Patchwork @ 2019-02-11 12:12 UTC (permalink / raw)
To: Tvrtko Ursulin; +Cc: igt-dev
== Series Details ==
Series: series starting with [1/2] tools/intel_gpu_top: Add support for stdout logging
URL : https://patchwork.freedesktop.org/series/56490/
State : success
== Summary ==
CI Bug Log - changes from CI_DRM_5586 -> IGTPW_2371
====================================================
Summary
-------
**SUCCESS**
No regressions found.
External URL: https://patchwork.freedesktop.org/api/1.0/series/56490/revisions/1/mbox/
Known issues
------------
Here are the changes found in IGTPW_2371 that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@kms_busy@basic-flip-a:
- fi-gdg-551: PASS -> FAIL [fdo#103182]
* igt@kms_chamelium@hdmi-hpd-fast:
- fi-kbl-7500u: PASS -> FAIL [fdo#109485]
* igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
- fi-byt-clapper: PASS -> FAIL [fdo#103191] / [fdo#107362]
#### Possible fixes ####
* igt@gem_exec_suspend@basic-s4-devices:
- fi-blb-e6850: INCOMPLETE [fdo#107718] -> PASS
* igt@kms_chamelium@dp-edid-read:
- fi-kbl-7500u: FAIL -> PASS
* igt@kms_pipe_crc_basic@hang-read-crc-pipe-a:
- fi-byt-clapper: FAIL [fdo#103191] / [fdo#107362] -> PASS
* igt@prime_vgem@basic-fence-flip:
- fi-gdg-551: FAIL [fdo#103182] -> PASS
{name}: This element is suppressed. This means it is ignored when computing
the status of the difference (SUCCESS, WARNING, or FAILURE).
[fdo#103182]: https://bugs.freedesktop.org/show_bug.cgi?id=103182
[fdo#103191]: https://bugs.freedesktop.org/show_bug.cgi?id=103191
[fdo#107362]: https://bugs.freedesktop.org/show_bug.cgi?id=107362
[fdo#107718]: https://bugs.freedesktop.org/show_bug.cgi?id=107718
[fdo#108569]: https://bugs.freedesktop.org/show_bug.cgi?id=108569
[fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
[fdo#109278]: https://bugs.freedesktop.org/show_bug.cgi?id=109278
[fdo#109485]: https://bugs.freedesktop.org/show_bug.cgi?id=109485
Participating hosts (47 -> 42)
------------------------------
Missing (5): fi-ilk-m540 fi-hsw-4200u fi-bsw-cyan fi-ctg-p8600 fi-icl-y
Build changes
-------------
* IGT: IGT_4816 -> IGTPW_2371
CI_DRM_5586: e54b6a76e1a6d0e4ba8b56f149673ea9907b2c59 @ git://anongit.freedesktop.org/gfx-ci/linux
IGTPW_2371: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2371/
IGT_4816: f62577c85c9ef0539d468d6fad105b706a15139c @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2371/
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
2019-02-11 11:45 [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging Tvrtko Ursulin
2019-02-11 11:45 ` [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability Tvrtko Ursulin
2019-02-11 12:12 ` [igt-dev] ✓ Fi.CI.BAT: success for series starting with [1/2] tools/intel_gpu_top: Add support for stdout logging Patchwork
@ 2019-02-11 12:21 ` Chris Wilson
2019-02-11 17:08 ` [Intel-gfx] " Tvrtko Ursulin
2019-02-11 14:09 ` [igt-dev] ✓ Fi.CI.IGT: success for series starting with [1/2] " Patchwork
3 siblings, 1 reply; 8+ messages in thread
From: Chris Wilson @ 2019-02-11 12:21 UTC (permalink / raw)
To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi, Tvrtko Ursulin
Quoting Tvrtko Ursulin (2019-02-11 11:45:22)
> +static enum {
> + INTERACTIVE,
> + STDOUT,
> + JSON
> +} output_mode;
> +
> +struct cnt_item {
> + struct pmu_counter *pmu;
/* "%*.*f", fmt_d, fmt_dd, X */
I tried fmt_d == fmt_width and fmt_dd == fmt_decimals
It's called field width and precision in the manpage, so
maybe fmt_width and fmt_precision.
Having figured that out, the use makes sense.
> + unsigned int fmt_d;
> + unsigned int fmt_dd;
> + double d;
> + double t;
> + double s;
> + const char *name;
> + const char *unit;
> +
> + /* Internal fields. */
> + char buf[16];
> +};
> +
> +struct cnt_group {
> + const char *name;
> + const char *display_name;
> + struct cnt_item *items;
> +};
> +
> +static unsigned int json_indent_level;
> +
> +static const char *json_indent[] = {
> + "",
> + "\t",
> + "\t\t",
> + "\t\t\t",
> + "\t\t\t\t",
> + "\t\t\t\t\t",
> +};
> +
> +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
> +
> +static unsigned int json_prev_struct_members;
> +static unsigned int json_struct_members;
> +
> +static void
> +json_open_struct(const char *name)
> +{
> + assert(json_indent_level < ARRAY_SIZE(json_indent));
> +
> + json_prev_struct_members = json_struct_members;
> + json_struct_members = 0;
> +
> + if (name)
> + printf("%s%s\"%s\": {\n",
> + json_prev_struct_members ? ",\n" : "",
> + json_indent[json_indent_level],
"%*s", json_indent_level, "\t\t\t\t\t"
didn't stick?
I could follow the flow, dug into a few of the details, and only have
some nits. One thing, could we feed in a perf record? I was thinking for
testing the various output modes with known data, but may also be useful
for replay.
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [Intel-gfx] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
2019-02-11 12:21 ` [igt-dev] [IGT 1/2] " Chris Wilson
@ 2019-02-11 17:08 ` Tvrtko Ursulin
0 siblings, 0 replies; 8+ messages in thread
From: Tvrtko Ursulin @ 2019-02-11 17:08 UTC (permalink / raw)
To: Chris Wilson, igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi
On 11/02/2019 12:21, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2019-02-11 11:45:22)
>> +static enum {
>> + INTERACTIVE,
>> + STDOUT,
>> + JSON
>> +} output_mode;
>> +
>> +struct cnt_item {
>> + struct pmu_counter *pmu;
>
> /* "%*.*f", fmt_d, fmt_dd, X */
>
> I tried fmt_d == fmt_width and fmt_dd == fmt_decimals
>
> It's called field width and precision in the manpage, so
> maybe fmt_width and fmt_precision.
Makes sense indeed.
> Having figured that out, the use makes sense.
>
>> + unsigned int fmt_d;
>> + unsigned int fmt_dd;
>> + double d;
>> + double t;
>> + double s;
>> + const char *name;
>> + const char *unit;
>> +
>> + /* Internal fields. */
>> + char buf[16];
>> +};
>> +
>> +struct cnt_group {
>> + const char *name;
>> + const char *display_name;
>> + struct cnt_item *items;
>> +};
>> +
>> +static unsigned int json_indent_level;
>> +
>> +static const char *json_indent[] = {
>> + "",
>> + "\t",
>> + "\t\t",
>> + "\t\t\t",
>> + "\t\t\t\t",
>> + "\t\t\t\t\t",
>> +};
>> +
>> +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
>> +
>> +static unsigned int json_prev_struct_members;
>> +static unsigned int json_struct_members;
>> +
>> +static void
>> +json_open_struct(const char *name)
>> +{
>> + assert(json_indent_level < ARRAY_SIZE(json_indent));
>> +
>> + json_prev_struct_members = json_struct_members;
>> + json_struct_members = 0;
>> +
>> + if (name)
>> + printf("%s%s\"%s\": {\n",
>> + json_prev_struct_members ? ",\n" : "",
>> + json_indent[json_indent_level],
>
> "%*s", json_indent_level, "\t\t\t\t\t"
> didn't stick?
No, I lost patience trying to make it do what I want. AFAIR it insisted
on right justifying and the negative count also did not work.
> I could follow the flow, dug into a few of the details, and only have
> some nits. One thing, could we feed in a perf record? I was thinking for
> testing the various output modes with known data, but may also be useful
> for replay.
Could do, should be too hard. Writing tests for tool output though
sounds like something I won't have time to do in the near future.
> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Thanks, I'll keep it when I fix the asprintf return value inspection
which I butchered in a hasty copy and paste work.
Regards,
Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 8+ messages in thread
* [igt-dev] ✓ Fi.CI.IGT: success for series starting with [1/2] tools/intel_gpu_top: Add support for stdout logging
2019-02-11 11:45 [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging Tvrtko Ursulin
` (2 preceding siblings ...)
2019-02-11 12:21 ` [igt-dev] [IGT 1/2] " Chris Wilson
@ 2019-02-11 14:09 ` Patchwork
3 siblings, 0 replies; 8+ messages in thread
From: Patchwork @ 2019-02-11 14:09 UTC (permalink / raw)
To: Tvrtko Ursulin; +Cc: igt-dev
== Series Details ==
Series: series starting with [1/2] tools/intel_gpu_top: Add support for stdout logging
URL : https://patchwork.freedesktop.org/series/56490/
State : success
== Summary ==
CI Bug Log - changes from CI_DRM_5586_full -> IGTPW_2371_full
====================================================
Summary
-------
**SUCCESS**
No regressions found.
External URL: https://patchwork.freedesktop.org/api/1.0/series/56490/revisions/1/mbox/
Known issues
------------
Here are the changes found in IGTPW_2371_full that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@gem_ctx_isolation@vecs0-s3:
- shard-kbl: PASS -> INCOMPLETE [fdo#103665]
* igt@gem_exec_schedule@preempt-self-blt:
- shard-apl: PASS -> INCOMPLETE [fdo#103927]
* igt@kms_atomic_transition@plane-all-modeset-transition-fencing:
- shard-hsw: PASS -> DMESG-WARN [fdo#102614] / [fdo#109225]
* igt@kms_busy@extended-modeset-hang-newfb-render-a:
- shard-hsw: PASS -> DMESG-WARN [fdo#107956]
* igt@kms_busy@extended-pageflip-modeset-hang-oldfb-render-b:
- shard-snb: PASS -> DMESG-WARN [fdo#107956]
* igt@kms_ccs@pipe-b-crc-sprite-planes-basic:
- shard-kbl: PASS -> FAIL [fdo#107725] / [fdo#108145]
* igt@kms_cursor_crc@cursor-128x128-sliding:
- shard-kbl: PASS -> FAIL [fdo#103232] +2
* igt@kms_cursor_crc@cursor-256x256-random:
- shard-apl: PASS -> FAIL [fdo#103232] +3
- shard-kbl: NOTRUN -> FAIL [fdo#103232]
* igt@kms_cursor_crc@cursor-64x64-suspend:
- shard-apl: PASS -> FAIL [fdo#103191] / [fdo#103232]
- shard-kbl: PASS -> FAIL [fdo#103191] / [fdo#103232]
* igt@kms_cursor_crc@cursor-alpha-opaque:
- shard-kbl: PASS -> FAIL [fdo#109350]
- shard-apl: PASS -> FAIL [fdo#109350]
* igt@kms_cursor_crc@cursor-size-change:
- shard-glk: PASS -> FAIL [fdo#103232]
* igt@kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-draw-pwrite:
- shard-apl: PASS -> FAIL [fdo#103167] +2
* igt@kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-mmap-cpu:
- shard-glk: PASS -> FAIL [fdo#103167] +6
* igt@kms_plane@plane-position-covered-pipe-c-planes:
- shard-apl: PASS -> FAIL [fdo#103166] +2
* igt@kms_plane_alpha_blend@pipe-b-constant-alpha-max:
- shard-glk: PASS -> FAIL [fdo#108145] +2
* igt@kms_plane_alpha_blend@pipe-c-alpha-opaque-fb:
- shard-apl: PASS -> FAIL [fdo#108145]
* igt@kms_plane_multiple@atomic-pipe-b-tiling-x:
- shard-glk: PASS -> FAIL [fdo#103166] +4
* igt@kms_setmode@basic:
- shard-hsw: PASS -> FAIL [fdo#99912]
- shard-kbl: PASS -> FAIL [fdo#99912]
#### Possible fixes ####
* igt@gem_exec_blt@dumb-buf-max:
- shard-glk: DMESG-WARN [fdo#105763] / [fdo#106538] -> PASS
* igt@i915_suspend@fence-restore-untiled:
- shard-snb: INCOMPLETE [fdo#105411] -> PASS
* igt@kms_busy@extended-pageflip-modeset-hang-oldfb-render-c:
- shard-apl: DMESG-WARN [fdo#107956] -> PASS
* igt@kms_cursor_crc@cursor-128x128-random:
- shard-apl: FAIL [fdo#103232] -> PASS +6
- shard-kbl: FAIL [fdo#103232] -> PASS +2
* igt@kms_cursor_crc@cursor-128x128-suspend:
- shard-apl: FAIL [fdo#103191] / [fdo#103232] -> PASS +1
- shard-kbl: FAIL [fdo#103191] / [fdo#103232] -> PASS
* igt@kms_cursor_legacy@cursor-vs-flip-atomic:
- shard-hsw: FAIL [fdo#103355] -> PASS
* igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-move:
- shard-glk: FAIL [fdo#103167] -> PASS +2
* igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-onoff:
- shard-apl: FAIL [fdo#103167] -> PASS +2
* igt@kms_plane_multiple@atomic-pipe-b-tiling-y:
- shard-kbl: FAIL [fdo#103166] -> PASS +1
* igt@kms_plane_multiple@atomic-pipe-c-tiling-yf:
- shard-apl: FAIL [fdo#103166] -> PASS +6
* igt@kms_setmode@basic:
- shard-apl: FAIL [fdo#99912] -> PASS
* igt@pm_rc6_residency@rc6-accuracy:
- shard-snb: {SKIP} [fdo#109271] -> PASS
#### Warnings ####
* igt@i915_suspend@shrink:
- shard-hsw: DMESG-WARN [fdo#109244] -> INCOMPLETE [fdo#103540] / [fdo#106886]
{name}: This element is suppressed. This means it is ignored when computing
the status of the difference (SUCCESS, WARNING, or FAILURE).
[fdo#102614]: https://bugs.freedesktop.org/show_bug.cgi?id=102614
[fdo#103166]: https://bugs.freedesktop.org/show_bug.cgi?id=103166
[fdo#103167]: https://bugs.freedesktop.org/show_bug.cgi?id=103167
[fdo#103191]: https://bugs.freedesktop.org/show_bug.cgi?id=103191
[fdo#103232]: https://bugs.freedesktop.org/show_bug.cgi?id=103232
[fdo#103355]: https://bugs.freedesktop.org/show_bug.cgi?id=103355
[fdo#103540]: https://bugs.freedesktop.org/show_bug.cgi?id=103540
[fdo#103665]: https://bugs.freedesktop.org/show_bug.cgi?id=103665
[fdo#103927]: https://bugs.freedesktop.org/show_bug.cgi?id=103927
[fdo#105411]: https://bugs.freedesktop.org/show_bug.cgi?id=105411
[fdo#105763]: https://bugs.freedesktop.org/show_bug.cgi?id=105763
[fdo#106538]: https://bugs.freedesktop.org/show_bug.cgi?id=106538
[fdo#106886]: https://bugs.freedesktop.org/show_bug.cgi?id=106886
[fdo#107725]: https://bugs.freedesktop.org/show_bug.cgi?id=107725
[fdo#107956]: https://bugs.freedesktop.org/show_bug.cgi?id=107956
[fdo#108145]: https://bugs.freedesktop.org/show_bug.cgi?id=108145
[fdo#109225]: https://bugs.freedesktop.org/show_bug.cgi?id=109225
[fdo#109244]: https://bugs.freedesktop.org/show_bug.cgi?id=109244
[fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
[fdo#109278]: https://bugs.freedesktop.org/show_bug.cgi?id=109278
[fdo#109350]: https://bugs.freedesktop.org/show_bug.cgi?id=109350
[fdo#99912]: https://bugs.freedesktop.org/show_bug.cgi?id=99912
Participating hosts (7 -> 5)
------------------------------
Missing (2): shard-skl shard-iclb
Build changes
-------------
* IGT: IGT_4816 -> IGTPW_2371
* Piglit: piglit_4509 -> None
CI_DRM_5586: e54b6a76e1a6d0e4ba8b56f149673ea9907b2c59 @ git://anongit.freedesktop.org/gfx-ci/linux
IGTPW_2371: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2371/
IGT_4816: f62577c85c9ef0539d468d6fad105b706a15139c @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2371/
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 8+ messages in thread
* [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
@ 2019-02-08 12:03 Tvrtko Ursulin
2019-02-08 12:03 ` [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability Tvrtko Ursulin
0 siblings, 1 reply; 8+ messages in thread
From: Tvrtko Ursulin @ 2019-02-08 12:03 UTC (permalink / raw)
To: igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi, Tvrtko Ursulin
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Two new output modes are added: listing of text data to standard out (-l
on the command line), and dumping of JSON formatted records (-J), also to
standard out.
The first mode is selected automatically when non-interactive standard out
is detected.
Example of text output:
Freq MHz IRQ RC6 Power IMC MiB/s RCS/0 BCS/0 VCS/0 VCS/1 VECS/0
req act /s % W rd wr % se wa % se wa % se wa % se wa % se wa
0 0 0 0 0.00 360 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 35 2 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 34 2 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 143 6 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 169 7 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
350 350 0 100 0.00 169 7 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0 0.00 0 0
Example of JSON output:
{
"period": {
"duration": 1002.525224,
"unit": "ms"
},
"frequency": {
"requested": 349.118398,
"actual": 349.118398,
"unit": "MHz"
},
"interrupts": {
"count": 0.000000,
"unit": "irq/s"
},
"rc6": {
"value": 99.897752,
"unit": "%"
},
"power": {
"value": 0.000000,
"unit": "W"
},
"imc-bandwidth": {
"reads": 149.683843,
"writes": 6.104093,
"unit": "MiB/s"
},
"engines": {
"Render/3D/0": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
},
"Blitter/0": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
},
"Video/0": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
},
"Video/1": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
},
"VideoEnhance/0": {
"busy": 0.000000,
"sema": 0.000000,
"wait": 0.000000,
"unit": "%"
}
}
}
v2:
* Show example output in commit message.
* Install signal handler to complete output on SIGINT. (Chris Wilson)
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
References: https://bugs.freedesktop.org/show_bug.cgi?id=108689
Cc: Eero Tamminen <eero.t.tamminen@intel.com>
Cc: 3.14pi@ukr.net
Cc: Chris Wilson <chris@chris-wilson.co.uk>
---
man/intel_gpu_top.rst | 9 +-
tools/intel_gpu_top.c | 761 +++++++++++++++++++++++++++++++++++-------
2 files changed, 650 insertions(+), 120 deletions(-)
diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index 19c712307d28..d5bda093c8e8 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -7,9 +7,9 @@ Display a top-like summary of Intel GPU usage
---------------------------------------------
.. include:: defs.rst
:Author: IGT Developers <igt-dev@lists.freedesktop.org>
-:Date: 2018-04-04
+:Date: 2019-02-08
:Version: |PACKAGE_STRING|
-:Copyright: 2009,2011,2012,2016,2018 Intel Corporation
+:Copyright: 2009,2011,2012,2016,2018,2019 Intel Corporation
:Manual section: |MANUAL_SECTION|
:Manual group: |MANUAL_GROUP|
@@ -31,6 +31,11 @@ OPTIONS
-s <ms>
Refresh period in milliseconds.
+-l
+ List text data to standard out.
+
+-J
+ Output JSON formatted data to standard output.
-h
Show help text.
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index b923c3cfbe97..807d518aaf87 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -1,5 +1,5 @@
/*
- * Copyright © 2007-2018 Intel Corporation
+ * Copyright © 2007-2019 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -19,10 +19,6 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Eric Anholt <eric@anholt.net>
- * Eugeni Dodonov <eugeni.dodonov@intel.com>
*/
#include <stdio.h>
@@ -41,6 +37,8 @@
#include <errno.h>
#include <math.h>
#include <locale.h>
+#include <limits.h>
+#include <signal.h>
#include "igt_perf.h"
@@ -59,6 +57,7 @@ struct pmu_counter {
struct engine {
const char *name;
const char *display_name;
+ const char *short_name;
unsigned int class;
unsigned int instance;
@@ -142,6 +141,22 @@ static const char *class_display_name(unsigned int class)
}
}
+static const char *class_short_name(unsigned int class)
+{
+ switch (class) {
+ case I915_ENGINE_CLASS_RENDER:
+ return "RCS";
+ case I915_ENGINE_CLASS_COPY:
+ return "BCS";
+ case I915_ENGINE_CLASS_VIDEO:
+ return "VCS";
+ case I915_ENGINE_CLASS_VIDEO_ENHANCE:
+ return "VECS";
+ default:
+ return "UNKN";
+ }
+}
+
static int engine_cmp(const void *__a, const void *__b)
{
const struct engine *a = (struct engine *)__a;
@@ -227,7 +242,6 @@ static struct engines *discover_engines(void)
ret = ENOBUFS;
break;
}
- ret = 0;
engine->display_name = strdup(buf);
if (!engine->display_name) {
@@ -235,6 +249,20 @@ static struct engines *discover_engines(void)
break;
}
+ ret = snprintf(buf, sizeof(buf), "%s/%u",
+ class_short_name(engine->class),
+ engine->instance);
+ if (ret < 0 || ret == sizeof(buf)) {
+ ret = ENOBUFS;
+ break;
+ }
+
+ engine->short_name = strdup(buf);
+ if (!engine->short_name) {
+ ret = errno;
+ break;
+ }
+
engines->num_engines++;
engines = realloc(engines, sizeof(struct engines) +
engines->num_engines * sizeof(struct engine));
@@ -242,6 +270,8 @@ static struct engines *discover_engines(void)
ret = errno;
break;
}
+
+ ret = 0;
}
if (ret) {
@@ -551,7 +581,7 @@ static uint64_t pmu_read_multi(int fd, unsigned int num, uint64_t *val)
return buf[1];
}
-static double __pmu_calc(struct pmu_pair *p, double d, double t, double s)
+static double pmu_calc(struct pmu_pair *p, double d, double t, double s)
{
double v;
@@ -576,30 +606,6 @@ static void fill_str(char *buf, unsigned int bufsz, char c, unsigned int num)
*buf = 0;
}
-static void pmu_calc(struct pmu_counter *cnt,
- char *buf, unsigned int bufsz,
- unsigned int width, unsigned width_dec,
- double d, double t, double s)
-{
- double val;
- int len;
-
- assert(bufsz >= (width + width_dec + 1));
-
- if (!cnt->present) {
- fill_str(buf, bufsz, '-', width + width_dec);
- return;
- }
-
- val = __pmu_calc(&cnt->val, d, t, s);
-
- len = snprintf(buf, bufsz, "%*.*f", width + width_dec, width_dec, val);
- if (len < 0 || len == bufsz) {
- fill_str(buf, bufsz, 'X', width + width_dec);
- return;
- }
-}
-
static uint64_t __pmu_read_single(int fd, uint64_t *ts)
{
uint64_t data[2] = { };
@@ -697,11 +703,559 @@ usage(const char *appname)
"\n"
"\tThe following parameters are optional:\n\n"
"\t[-s <ms>] Refresh period in milliseconds (default %ums).\n"
+ "\t[-l] List data to standard out.\n"
+ "\t[-J] JSON data to standard out.\n"
"\t[-h] Show this help text.\n"
"\n",
appname, DEFAULT_PERIOD_MS);
}
+static enum {
+ INTERACTIVE,
+ STDOUT,
+ JSON
+} output_mode;
+
+struct cnt_item {
+ struct pmu_counter *pmu;
+ unsigned int fmt_d;
+ unsigned int fmt_dd;
+ double d;
+ double t;
+ double s;
+ const char *name;
+ const char *unit;
+
+ /* Internal fields. */
+ char buf[16];
+};
+
+struct cnt_group {
+ const char *name;
+ const char *display_name;
+ struct cnt_item *items;
+};
+
+static unsigned int json_indent_level;
+
+static const char *json_indent[] = {
+ "",
+ "\t",
+ "\t\t",
+ "\t\t\t",
+ "\t\t\t\t",
+ "\t\t\t\t\t",
+};
+
+#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
+
+static unsigned int json_prev_struct_members;
+static unsigned int json_struct_members;
+
+static void
+json_open_struct(const char *name)
+{
+ assert(json_indent_level < ARRAY_SIZE(json_indent));
+
+ json_prev_struct_members = json_struct_members;
+ json_struct_members = 0;
+
+ if (name)
+ printf("%s%s\"%s\": {\n",
+ json_prev_struct_members ? ",\n" : "",
+ json_indent[json_indent_level],
+ name);
+ else
+ printf("%s\n%s{\n",
+ json_prev_struct_members ? "," : "",
+ json_indent[json_indent_level]);
+
+ json_indent_level++;
+}
+
+static void
+json_close_struct(void)
+{
+ assert(json_indent_level > 0);
+
+ printf("\n%s}", json_indent[--json_indent_level]);
+
+ if (json_indent_level == 0)
+ fflush(stdout);
+}
+
+static unsigned int
+json_add_member(const struct cnt_group *parent, struct cnt_item *item,
+ unsigned int headers)
+{
+ assert(json_indent_level < ARRAY_SIZE(json_indent));
+
+ printf("%s%s\"%s\": ",
+ json_struct_members ? ",\n" : "",
+ json_indent[json_indent_level], item->name);
+
+ json_struct_members++;
+
+ if (!strcmp(item->name, "unit"))
+ printf("\"%s\"", item->unit);
+ else
+ printf("%f",
+ pmu_calc(&item->pmu->val, item->d, item->t, item->s));
+
+ return 1;
+}
+
+static unsigned int stdout_level;
+
+#define STDOUT_HEADER_REPEAT 20
+static unsigned int stdout_lines = STDOUT_HEADER_REPEAT;
+
+static void
+stdout_open_struct(const char *name)
+{
+ stdout_level++;
+ assert(stdout_level > 0);
+}
+
+static void
+stdout_close_struct(void)
+{
+ assert(stdout_level > 0);
+ if (--stdout_level == 0) {
+ stdout_lines++;
+ printf("\n");
+ fflush(stdout);
+ }
+}
+
+static unsigned int
+stdout_add_member(const struct cnt_group *parent, struct cnt_item *item,
+ unsigned int headers)
+{
+ unsigned int fmt_tot = item->fmt_d + (item->fmt_dd ? 1 : 0);
+ char buf[fmt_tot + 1];
+ double val;
+ int len;
+
+ if (!item->pmu)
+ return 0;
+ else if (!item->pmu->present)
+ return 0;
+
+ if (headers == 1) {
+ unsigned int grp_tot = 0;
+ struct cnt_item *it;
+
+ if (item != parent->items)
+ return 0;
+
+ for (it = parent->items; it->pmu; it++) {
+ if (!it->pmu->present)
+ continue;
+
+ grp_tot += 1 + it->fmt_d + (it->fmt_dd ? 1 : 0);
+ }
+
+ printf("%*s ", grp_tot - 1, parent->display_name);
+ return 0;
+ } else if (headers == 2) {
+ printf("%*s ", fmt_tot, item->unit ?: item->name);
+ return 0;
+ }
+
+ val = pmu_calc(&item->pmu->val, item->d, item->t, item->s);
+
+ len = snprintf(buf, sizeof(buf), "%*.*f", fmt_tot, item->fmt_dd, val);
+ if (len < 0 || len == sizeof(buf))
+ fill_str(buf, sizeof(buf), 'X', fmt_tot);
+
+ len = printf("%s ", buf);
+
+ return len > 0 ? len : 0;
+}
+
+static void
+term_open_struct(const char *name)
+{
+}
+
+static void
+term_close_struct(void)
+{
+}
+
+static unsigned int
+term_add_member(const struct cnt_group *parent, struct cnt_item *item,
+ unsigned int headers)
+{
+ unsigned int fmt_tot = item->fmt_d + (item->fmt_dd ? 1 : 0);
+ double val;
+ int len;
+
+ if (!item->pmu)
+ return 0;
+
+ assert(fmt_tot <= sizeof(item->buf));
+
+ if (!item->pmu->present) {
+ fill_str(item->buf, sizeof(item->buf), '-', fmt_tot);
+ return 1;
+ }
+
+ val = pmu_calc(&item->pmu->val, item->d, item->t, item->s);
+ len = snprintf(item->buf, sizeof(item->buf),
+ "%*.*f",
+ fmt_tot, item->fmt_dd, val);
+
+ if (len < 0 || len == sizeof(item->buf))
+ fill_str(item->buf, sizeof(item->buf), 'X', fmt_tot);
+
+ return 1;
+}
+
+struct print_operations {
+ void (*open_struct)(const char *name);
+ void (*close_struct)(void);
+ unsigned int (*add_member)(const struct cnt_group *parent,
+ struct cnt_item *item,
+ unsigned int headers);
+ bool (*print_group)(struct cnt_group *group, unsigned int headers);
+};
+
+static const struct print_operations *pops;
+
+static unsigned int
+present_in_group(const struct cnt_group *grp)
+{
+ unsigned int present = 0;
+ struct cnt_item *item;
+
+ for (item = grp->items; item->name; item++) {
+ if (item->pmu && item->pmu->present)
+ present++;
+ }
+
+ return present;
+}
+
+static bool
+print_group(struct cnt_group *grp, unsigned int headers)
+{
+ unsigned int consumed = 0;
+ struct cnt_item *item;
+
+ if (!present_in_group(grp))
+ return false;
+
+ pops->open_struct(grp->name);
+
+ for (item = grp->items; item->name; item++)
+ consumed += pops->add_member(grp, item, headers);
+
+ pops->close_struct();
+
+ return consumed;
+}
+
+static bool
+term_print_group(struct cnt_group *grp, unsigned int headers)
+{
+ unsigned int consumed = 0;
+ struct cnt_item *item;
+
+ pops->open_struct(grp->name);
+
+ for (item = grp->items; item->name; item++)
+ consumed += pops->add_member(grp, item, headers);
+
+ pops->close_struct();
+
+ return consumed;
+}
+
+static const struct print_operations json_pops = {
+ .open_struct = json_open_struct,
+ .close_struct = json_close_struct,
+ .add_member = json_add_member,
+ .print_group = print_group,
+};
+
+static const struct print_operations stdout_pops = {
+ .open_struct = stdout_open_struct,
+ .close_struct = stdout_close_struct,
+ .add_member = stdout_add_member,
+ .print_group = print_group,
+};
+
+static const struct print_operations term_pops = {
+ .open_struct = term_open_struct,
+ .close_struct = term_close_struct,
+ .add_member = term_add_member,
+ .print_group = term_print_group,
+};
+
+static bool print_groups(struct cnt_group **groups)
+{
+ unsigned int headers = stdout_lines % STDOUT_HEADER_REPEAT + 1;
+ bool print_data = true;
+
+ if (output_mode == STDOUT && (headers == 1 || headers == 2)) {
+ for (struct cnt_group **grp = groups; *grp; grp++)
+ print_data = pops->print_group(*grp, headers);
+ }
+
+ for (struct cnt_group **grp = groups; print_data && *grp; grp++)
+ pops->print_group(*grp, false);
+
+ return print_data;
+}
+
+static int
+print_header(struct engines *engines, double t,
+ int lines, int con_w, int con_h, bool *consumed)
+{
+ struct pmu_counter fake_pmu = {
+ .present = true,
+ .val.cur = 1,
+ };
+ struct cnt_item period_items[] = {
+ { &fake_pmu, 0, 0, 1.0, 1.0, t * 1e3, "duration" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "ms" },
+ { },
+ };
+ struct cnt_group period_group = {
+ .name = "period",
+ .items = period_items,
+ };
+ struct cnt_item freq_items[] = {
+ { &engines->freq_req, 4, 0, 1.0, t, 1, "requested", "req" },
+ { &engines->freq_act, 4, 0, 1.0, t, 1, "actual", "act" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "MHz" },
+ { },
+ };
+ struct cnt_group freq_group = {
+ .name = "frequency",
+ .display_name = "Freq MHz",
+ .items = freq_items,
+ };
+ struct cnt_item irq_items[] = {
+ { &engines->irq, 8, 0, 1.0, t, 1, "count", "/s" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "irq/s" },
+ { },
+ };
+ struct cnt_group irq_group = {
+ .name = "interrupts",
+ .display_name = "IRQ",
+ .items = irq_items,
+ };
+ struct cnt_item rc6_items[] = {
+ { &engines->rc6, 3, 0, 1e9, t, 100, "value", "%" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "%" },
+ { },
+ };
+ struct cnt_group rc6_group = {
+ .name = "rc6",
+ .display_name = "RC6",
+ .items = rc6_items,
+ };
+ struct cnt_item power_items[] = {
+ { &engines->rapl, 4, 2, 1.0, t, engines->rapl_scale, "value",
+ "W" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "W" },
+ { },
+ };
+ struct cnt_group power_group = {
+ .name = "power",
+ .display_name = "Power",
+ .items = power_items,
+ };
+ struct cnt_group *groups[] = {
+ &period_group,
+ &freq_group,
+ &irq_group,
+ &rc6_group,
+ &power_group,
+ NULL
+ };
+
+ if (output_mode != JSON)
+ memmove(&groups[0], &groups[1],
+ sizeof(groups) - sizeof(groups[0]));
+
+ pops->open_struct(NULL);
+
+ *consumed = print_groups(groups);
+
+ if (output_mode == INTERACTIVE) {
+ printf("\033[H\033[J");
+
+ if (lines++ < con_h)
+ printf("intel-gpu-top - %s/%s MHz; %s%% RC6; %s %s; %s irqs/s\n",
+ freq_items[1].buf, freq_items[0].buf,
+ rc6_items[0].buf, power_items[0].buf,
+ engines->rapl_unit,
+ irq_items[0].buf);
+
+ if (lines++ < con_h)
+ printf("\n");
+ }
+
+ return lines;
+}
+
+static int
+print_imc(struct engines *engines, double t, int lines, int con_w, int con_h)
+{
+ struct cnt_item imc_items[] = {
+ { &engines->imc_reads, 6, 0, 1.0, t, engines->imc_reads_scale,
+ "reads", "rd" },
+ { &engines->imc_writes, 6, 0, 1.0, t, engines->imc_writes_scale,
+ "writes", "wr" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit" },
+ { },
+ };
+ struct cnt_group imc_group = {
+ .name = "imc-bandwidth",
+ .items = imc_items,
+ };
+ struct cnt_group *groups[] = {
+ &imc_group,
+ NULL
+ };
+ int ret;
+
+ ret = asprintf((char **)&imc_group.display_name, "IMC %s/s",
+ engines->imc_reads_unit);
+ assert(ret >= 0);
+
+ ret = asprintf((char **)&imc_items[2].unit, "%s/s",
+ engines->imc_reads_unit);
+ assert(ret >= 0);
+
+ print_groups(groups);
+
+ free((void *)imc_group.display_name);
+ free((void *)imc_items[2].unit);
+
+ if (output_mode == INTERACTIVE) {
+ if (lines++ < con_h)
+ printf(" IMC reads: %s %s/s\n",
+ imc_items[0].buf, engines->imc_reads_unit);
+
+ if (lines++ < con_h)
+ printf(" IMC writes: %s %s/s\n",
+ imc_items[1].buf, engines->imc_writes_unit);
+
+ if (lines++ < con_h)
+ printf("\n");
+ }
+
+ return lines;
+}
+
+static int
+print_engines_header(struct engines *engines, double t,
+ int lines, int con_w, int con_h)
+{
+ for (unsigned int i = 0;
+ i < engines->num_engines && lines < con_h;
+ i++) {
+ struct engine *engine = engine_ptr(engines, i);
+
+ if (!engine->num_counters)
+ continue;
+
+ pops->open_struct("engines");
+
+ if (output_mode == INTERACTIVE) {
+ const char *a = " ENGINE BUSY ";
+ const char *b = " MI_SEMA MI_WAIT";
+
+ printf("\033[7m%s%*s%s\033[0m\n",
+ a, (int)(con_w - 1 - strlen(a) - strlen(b)),
+ " ", b);
+
+ lines++;
+ }
+
+ break;
+ }
+
+ return lines;
+}
+
+static int
+print_engine(struct engines *engines, unsigned int i, double t,
+ int lines, int con_w, int con_h)
+{
+ struct engine *engine = engine_ptr(engines, i);
+ struct cnt_item engine_items[] = {
+ { &engine->busy, 6, 2, 1e9, t, 100, "busy", "%" },
+ { &engine->sema, 3, 0, 1e9, t, 100, "sema", "se" },
+ { &engine->wait, 3, 0, 1e9, t, 100, "wait", "wa" },
+ { NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "%" },
+ { },
+ };
+ struct cnt_group engine_group = {
+ .name = engine->display_name,
+ .display_name = engine->short_name,
+ .items = engine_items,
+ };
+ struct cnt_group *groups[] = {
+ &engine_group,
+ NULL
+ };
+
+ if (!engine->num_counters)
+ return lines;
+
+ print_groups(groups);
+
+ if (output_mode == INTERACTIVE) {
+ unsigned int max_w = con_w - 1;
+ unsigned int len;
+ char buf[128];
+ double val;
+
+ len = snprintf(buf, sizeof(buf), " %s%% %s%%",
+ engine_items[1].buf, engine_items[2].buf);
+
+ len += printf("%16s %s%% ",
+ engine->display_name, engine_items[0].buf);
+
+ val = pmu_calc(&engine->busy.val, 1e9, t, 100);
+ print_percentage_bar(val, max_w - len);
+
+ printf("%s\n", buf);
+
+ lines++;
+ }
+
+ return lines;
+}
+
+static int
+print_engines_footer(struct engines *engines, double t,
+ int lines, int con_w, int con_h)
+{
+ pops->close_struct();
+ pops->close_struct();
+
+ if (output_mode == INTERACTIVE) {
+ if (lines++ < con_h)
+ printf("\n");
+ }
+
+ return lines;
+}
+
+static bool stop_top;
+
+static void sigint_handler(int sig)
+{
+ stop_top = true;
+}
+
int main(int argc, char **argv)
{
unsigned int period_us = DEFAULT_PERIOD_MS * 1000;
@@ -711,11 +1265,17 @@ int main(int argc, char **argv)
int ret, ch;
/* Parse options */
- while ((ch = getopt(argc, argv, "s:h")) != -1) {
+ while ((ch = getopt(argc, argv, "s:Jlh")) != -1) {
switch (ch) {
case 's':
period_us = atoi(optarg) * 1000;
break;
+ case 'J':
+ output_mode = JSON;
+ break;
+ case 'l':
+ output_mode = STDOUT;
+ break;
case 'h':
usage(argv[0]);
exit(0);
@@ -726,6 +1286,31 @@ int main(int argc, char **argv)
}
}
+ if (output_mode == INTERACTIVE && isatty(1) != 1)
+ output_mode = STDOUT;
+
+ if (output_mode != INTERACTIVE) {
+ sighandler_t sig = signal(SIGINT, sigint_handler);
+
+ if (sig == SIG_ERR)
+ fprintf(stderr, "Failed to install signal handler!\n");
+ }
+
+ switch (output_mode) {
+ case INTERACTIVE:
+ pops = &term_pops;
+ break;
+ case STDOUT:
+ pops = &stdout_pops;
+ break;
+ case JSON:
+ pops = &json_pops;
+ break;
+ default:
+ assert(0);
+ break;
+ };
+
engines = discover_engines();
if (!engines) {
fprintf(stderr,
@@ -743,21 +1328,16 @@ int main(int argc, char **argv)
pmu_sample(engines);
- for (;;) {
- double t;
-#define BUFSZ 16
- char freq[BUFSZ];
- char fact[BUFSZ];
- char irq[BUFSZ];
- char rc6[BUFSZ];
- char power[BUFSZ];
- char reads[BUFSZ];
- char writes[BUFSZ];
- struct winsize ws;
+ while (!stop_top) {
+ bool consumed = false;
int lines = 0;
+ struct winsize ws;
+ double t;
/* Update terminal size. */
- if (ioctl(0, TIOCGWINSZ, &ws) != -1) {
+ if (output_mode != INTERACTIVE) {
+ con_w = con_h = INT_MAX;
+ } else if (ioctl(0, TIOCGWINSZ, &ws) != -1) {
con_w = ws.ws_col;
con_h = ws.ws_row;
}
@@ -765,87 +1345,32 @@ int main(int argc, char **argv)
pmu_sample(engines);
t = (double)(engines->ts.cur - engines->ts.prev) / 1e9;
- printf("\033[H\033[J");
-
- 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);
- pmu_calc(&engines->irq, irq, BUFSZ, 8, 0, 1.0, t, 1);
- pmu_calc(&engines->rc6, rc6, BUFSZ, 3, 0, 1e9, t, 100);
- pmu_calc(&engines->rapl, power, BUFSZ, 4, 2, 1.0, t,
- engines->rapl_scale);
- pmu_calc(&engines->imc_reads, reads, BUFSZ, 6, 0, 1.0, t,
- engines->imc_reads_scale);
- pmu_calc(&engines->imc_writes, writes, BUFSZ, 6, 0, 1.0, t,
- engines->imc_writes_scale);
-
- if (lines++ < con_h)
- printf("intel-gpu-top - %s/%s MHz; %s%% RC6; %s %s; %s irqs/s\n",
- fact, freq, rc6, power, engines->rapl_unit, irq);
-
- if (lines++ < con_h)
- printf("\n");
-
- if (engines->imc_fd) {
- if (lines++ < con_h)
- printf(" IMC reads: %s %s/s\n",
- reads, engines->imc_reads_unit);
-
- if (lines++ < con_h)
- printf(" IMC writes: %s %s/s\n",
- writes, engines->imc_writes_unit);
-
- if (++lines < con_h)
- printf("\n");
- }
-
- for (i = 0; i < engines->num_engines; i++) {
- struct engine *engine = engine_ptr(engines, i);
-
- if (engine->num_counters && lines < con_h) {
- const char *a = " ENGINE BUSY ";
- const char *b = " MI_SEMA MI_WAIT";
-
- printf("\033[7m%s%*s%s\033[0m\n",
- a,
- (int)(con_w - 1 - strlen(a) - strlen(b)),
- " ", b);
- lines++;
- break;
- }
- }
-
- for (i = 0; i < engines->num_engines && lines < con_h; i++) {
- struct engine *engine = engine_ptr(engines, i);
- unsigned int max_w = con_w - 1;
- unsigned int len;
- char sema[BUFSZ];
- char wait[BUFSZ];
- char busy[BUFSZ];
- char buf[128];
- double val;
-
- if (!engine->num_counters)
- continue;
+ if (stop_top)
+ break;
- pmu_calc(&engine->sema, sema, BUFSZ, 3, 0, 1e9, t, 100);
- pmu_calc(&engine->wait, wait, BUFSZ, 3, 0, 1e9, t, 100);
- len = snprintf(buf, sizeof(buf), " %s%% %s%%",
- sema, wait);
+ while (!consumed) {
+ lines = print_header(engines, t, lines, con_w, con_h,
+ &consumed);
- pmu_calc(&engine->busy, busy, BUFSZ, 6, 2, 1e9, t,
- 100);
- len += printf("%16s %s%% ", engine->display_name, busy);
+ if (engines->imc_fd)
+ lines = print_imc(engines, t, lines, con_w,
+ con_h);
- val = __pmu_calc(&engine->busy.val, 1e9, t, 100);
- print_percentage_bar(val, max_w - len);
+ lines = print_engines_header(engines, t, lines, con_w,
+ con_h);
- printf("%s\n", buf);
+ for (i = 0;
+ i < engines->num_engines && lines < con_h;
+ i++)
+ lines = print_engine(engines, i, t, lines,
+ con_w, con_h);
- lines++;
+ lines = print_engines_footer(engines, t, lines, con_w,
+ con_h);
}
- if (lines++ < con_h)
- printf("\n");
+ if (stop_top)
+ break;
usleep(period_us);
}
--
2.19.1
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply related [flat|nested] 8+ messages in thread* [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability
2019-02-08 12:03 [igt-dev] [IGT 1/2] " Tvrtko Ursulin
@ 2019-02-08 12:03 ` Tvrtko Ursulin
2019-02-08 12:14 ` Chris Wilson
0 siblings, 1 reply; 8+ messages in thread
From: Tvrtko Ursulin @ 2019-02-08 12:03 UTC (permalink / raw)
To: igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi, Tvrtko Ursulin
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
A new -o command switch enables logging to a file.
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
References: https://bugs.freedesktop.org/show_bug.cgi?id=108689
Cc: Eero Tamminen <eero.t.tamminen@intel.com>
Cc: 3.14pi@ukr.net
Cc: Chris Wilson <chris@chris-wilson.co.uk>
---
man/intel_gpu_top.rst | 18 ++++++++-----
tools/intel_gpu_top.c | 63 ++++++++++++++++++++++++++++---------------
2 files changed, 52 insertions(+), 29 deletions(-)
diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index d5bda093c8e8..1313ef0bde5f 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -28,16 +28,20 @@ The tool gathers data using perf performance counters (PMU) exposed by i915 and
OPTIONS
=======
--s <ms>
- Refresh period in milliseconds.
+-h
+ Show help text.
+
+-J
+ Output JSON formatted data.
-l
- List text data to standard out.
+ List plain text data.
--J
- Output JSON formatted data to standard output.
--h
- Show help text.
+-o <file path>
+ Output to the specified file instead of standard output.
+
+-s <ms>
+ Refresh period in milliseconds.
LIMITATIONS
===========
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 807d518aaf87..ecbabfb3bc75 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -702,10 +702,11 @@ usage(const char *appname)
"Usage: %s [parameters]\n"
"\n"
"\tThe following parameters are optional:\n\n"
- "\t[-s <ms>] Refresh period in milliseconds (default %ums).\n"
- "\t[-l] List data to standard out.\n"
- "\t[-J] JSON data to standard out.\n"
"\t[-h] Show this help text.\n"
+ "\t[-J] Output JSON formatted data.\n"
+ "\t[-l] List plain text data.\n"
+ "\t[-o <file>] Output to specified file.\n"
+ "\t[-s <ms>] Refresh period in milliseconds (default %ums).\n"
"\n",
appname, DEFAULT_PERIOD_MS);
}
@@ -752,6 +753,8 @@ static const char *json_indent[] = {
static unsigned int json_prev_struct_members;
static unsigned int json_struct_members;
+FILE *out;
+
static void
json_open_struct(const char *name)
{
@@ -761,14 +764,14 @@ json_open_struct(const char *name)
json_struct_members = 0;
if (name)
- printf("%s%s\"%s\": {\n",
- json_prev_struct_members ? ",\n" : "",
- json_indent[json_indent_level],
- name);
+ fprintf(out, "%s%s\"%s\": {\n",
+ json_prev_struct_members ? ",\n" : "",
+ json_indent[json_indent_level],
+ name);
else
- printf("%s\n%s{\n",
- json_prev_struct_members ? "," : "",
- json_indent[json_indent_level]);
+ fprintf(out, "%s\n%s{\n",
+ json_prev_struct_members ? "," : "",
+ json_indent[json_indent_level]);
json_indent_level++;
}
@@ -778,7 +781,7 @@ json_close_struct(void)
{
assert(json_indent_level > 0);
- printf("\n%s}", json_indent[--json_indent_level]);
+ fprintf(out, "\n%s}", json_indent[--json_indent_level]);
if (json_indent_level == 0)
fflush(stdout);
@@ -790,17 +793,17 @@ json_add_member(const struct cnt_group *parent, struct cnt_item *item,
{
assert(json_indent_level < ARRAY_SIZE(json_indent));
- printf("%s%s\"%s\": ",
+ fprintf(out, "%s%s\"%s\": ",
json_struct_members ? ",\n" : "",
json_indent[json_indent_level], item->name);
json_struct_members++;
if (!strcmp(item->name, "unit"))
- printf("\"%s\"", item->unit);
+ fprintf(out, "\"%s\"", item->unit);
else
- printf("%f",
- pmu_calc(&item->pmu->val, item->d, item->t, item->s));
+ fprintf(out, "%f",
+ pmu_calc(&item->pmu->val, item->d, item->t, item->s));
return 1;
}
@@ -823,8 +826,8 @@ stdout_close_struct(void)
assert(stdout_level > 0);
if (--stdout_level == 0) {
stdout_lines++;
- printf("\n");
- fflush(stdout);
+ fputs("\n", out);
+ fflush(out);
}
}
@@ -856,10 +859,10 @@ stdout_add_member(const struct cnt_group *parent, struct cnt_item *item,
grp_tot += 1 + it->fmt_d + (it->fmt_dd ? 1 : 0);
}
- printf("%*s ", grp_tot - 1, parent->display_name);
+ fprintf(out, "%*s ", grp_tot - 1, parent->display_name);
return 0;
} else if (headers == 2) {
- printf("%*s ", fmt_tot, item->unit ?: item->name);
+ fprintf(out, "%*s ", fmt_tot, item->unit ?: item->name);
return 0;
}
@@ -869,7 +872,7 @@ stdout_add_member(const struct cnt_group *parent, struct cnt_item *item,
if (len < 0 || len == sizeof(buf))
fill_str(buf, sizeof(buf), 'X', fmt_tot);
- len = printf("%s ", buf);
+ len = fprintf(out, "%s ", buf);
return len > 0 ? len : 0;
}
@@ -1260,13 +1263,17 @@ int main(int argc, char **argv)
{
unsigned int period_us = DEFAULT_PERIOD_MS * 1000;
int con_w = -1, con_h = -1;
+ char *output_path = NULL;
struct engines *engines;
unsigned int i;
int ret, ch;
/* Parse options */
- while ((ch = getopt(argc, argv, "s:Jlh")) != -1) {
+ while ((ch = getopt(argc, argv, "o:s:Jlh")) != -1) {
switch (ch) {
+ case 'o':
+ output_path = optarg;
+ break;
case 's':
period_us = atoi(optarg) * 1000;
break;
@@ -1286,9 +1293,21 @@ int main(int argc, char **argv)
}
}
- if (output_mode == INTERACTIVE && isatty(1) != 1)
+ if (output_mode == INTERACTIVE && (output_path || isatty(1) != 1))
output_mode = STDOUT;
+ if (output_path) {
+ out = fopen(output_path, "w");
+
+ if (!out) {
+ fprintf(stderr, "Failed to open output file - '%s'!\n",
+ strerror(errno));
+ exit(1);
+ }
+ } else {
+ out = stdout;
+ }
+
if (output_mode != INTERACTIVE) {
sighandler_t sig = signal(SIGINT, sigint_handler);
--
2.19.1
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2019-02-11 17:08 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-02-11 11:45 [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging Tvrtko Ursulin
2019-02-11 11:45 ` [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability Tvrtko Ursulin
2019-02-11 12:12 ` [igt-dev] ✓ Fi.CI.BAT: success for series starting with [1/2] tools/intel_gpu_top: Add support for stdout logging Patchwork
2019-02-11 12:21 ` [igt-dev] [IGT 1/2] " Chris Wilson
2019-02-11 17:08 ` [Intel-gfx] " Tvrtko Ursulin
2019-02-11 14:09 ` [igt-dev] ✓ Fi.CI.IGT: success for series starting with [1/2] " Patchwork
-- strict thread matches above, loose matches on Subject: below --
2019-02-08 12:03 [igt-dev] [IGT 1/2] " Tvrtko Ursulin
2019-02-08 12:03 ` [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability Tvrtko Ursulin
2019-02-08 12:14 ` Chris Wilson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox