All of lore.kernel.org
 help / color / mirror / Atom feed
* [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
@ 2019-02-08 12:03 ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2019-02-08 12:03 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi

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

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
@ 2019-02-08 12:03 ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ 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] 20+ messages in thread

* [IGT 2/2] tools/intel_gpu_top: Add file output capability
  2019-02-08 12:03 ` [igt-dev] " Tvrtko Ursulin
@ 2019-02-08 12:03   ` Tvrtko Ursulin
  -1 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2019-02-08 12:03 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi

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

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability
@ 2019-02-08 12:03   ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ 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] 20+ messages in thread

* Re: [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
  2019-02-08 12:03 ` [igt-dev] " Tvrtko Ursulin
@ 2019-02-08 12:10   ` Chris Wilson
  -1 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2019-02-08 12:10 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi

Quoting Tvrtko Ursulin (2019-02-08 12:03:50)
> 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": {

Stale output? Didn't you switch to the bspec names?
"${class}${instance}": {

> +               ret = snprintf(buf, sizeof(buf), "%s/%u",

"%s/%u", we don't use a slash separator elsewhere?

> +                              class_short_name(engine->class),
> +                              engine->instance);
> +               if (ret < 0 || ret == sizeof(buf)) {
> +                       ret = ENOBUFS;
> +                       break;
> +               }
> +
> +               engine->short_name = strdup(buf);

asprintf(&engine->short_name ...);
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
@ 2019-02-08 12:10   ` Chris Wilson
  0 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2019-02-08 12:10 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2019-02-08 12:03:50)
> 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": {

Stale output? Didn't you switch to the bspec names?
"${class}${instance}": {

> +               ret = snprintf(buf, sizeof(buf), "%s/%u",

"%s/%u", we don't use a slash separator elsewhere?

> +                              class_short_name(engine->class),
> +                              engine->instance);
> +               if (ret < 0 || ret == sizeof(buf)) {
> +                       ret = ENOBUFS;
> +                       break;
> +               }
> +
> +               engine->short_name = strdup(buf);

asprintf(&engine->short_name ...);
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [IGT 2/2] tools/intel_gpu_top: Add file output capability
  2019-02-08 12:03   ` [igt-dev] " Tvrtko Ursulin
@ 2019-02-08 12:14     ` Chris Wilson
  -1 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2019-02-08 12:14 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi

Quoting Tvrtko Ursulin (2019-02-08 12:03:51)
> 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>

> -       if (output_mode == INTERACTIVE && isatty(1) != 1)
> +       if (output_mode == INTERACTIVE && (output_path || isatty(1) != 1))
>                 output_mode = STDOUT;
>  
> +       if (output_path) {

if (output_path && strcmp(output_path, "-")) { ?

"-o -" is common enough for forcing output even on the terminal.

Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] [IGT 2/2] tools/intel_gpu_top: Add file output capability
@ 2019-02-08 12:14     ` Chris Wilson
  0 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2019-02-08 12:14 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2019-02-08 12:03:51)
> 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>

> -       if (output_mode == INTERACTIVE && isatty(1) != 1)
> +       if (output_mode == INTERACTIVE && (output_path || isatty(1) != 1))
>                 output_mode = STDOUT;
>  
> +       if (output_path) {

if (output_path && strcmp(output_path, "-")) { ?

"-o -" is common enough for forcing output even on the terminal.

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] 20+ 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-08 12:03 ` [igt-dev] " Tvrtko Ursulin
                   ` (2 preceding siblings ...)
  (?)
@ 2019-02-08 12:29 ` Patchwork
  2019-02-25 10:04   ` Tvrtko Ursulin
  -1 siblings, 1 reply; 20+ messages in thread
From: Patchwork @ 2019-02-08 12:29 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/56393/
State : success

== Summary ==

CI Bug Log - changes from IGT_4814 -> IGTPW_2362
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/56393/revisions/1/mbox/

Known issues
------------

  Here are the changes found in IGTPW_2362 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@i915_selftest@live_evict:
    - fi-bsw-kefka:       PASS -> DMESG-WARN [fdo#107709]

  * igt@kms_chamelium@hdmi-hpd-fast:
    - fi-kbl-7500u:       PASS -> FAIL [fdo#109485]

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#107709]: https://bugs.freedesktop.org/show_bug.cgi?id=107709
  [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
  [fdo#109527]: https://bugs.freedesktop.org/show_bug.cgi?id=109527


Participating hosts (45 -> 42)
------------------------------

  Additional (3): fi-hsw-4770r fi-byt-j1900 fi-gdg-551 
  Missing    (6): fi-kbl-soraka fi-ilk-m540 fi-byt-squawks fi-bsw-cyan fi-kbl-x1275 fi-icl-y 


Build changes
-------------

    * IGT: IGT_4814 -> IGTPW_2362

  CI_DRM_5566: dc8d2739325432dd969028550e9e9b3070b76db1 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_2362: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2362/
  IGT_4814: cec046d72944a2d559ce5bd83358f08f20454005 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2362/
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
  2019-02-08 12:03 ` [igt-dev] " Tvrtko Ursulin
@ 2019-02-08 13:58   ` Eero Tamminen
  -1 siblings, 0 replies; 20+ messages in thread
From: Eero Tamminen @ 2019-02-08 13:58 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, 3.14pi

[-- Attachment #1: Type: text/plain, Size: 27819 bytes --]

Hi,

On 8.2.2019 14.03, Tvrtko Ursulin wrote:
> 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

Looks nice!

If you add '#' to the start of the header lines, one could use something 
like the attached shell script to convert the saved output to SVG graphs 
with GnuPlot.

Before including the script to igt, it would need to be modified to 
adapt to the number of engines, but maybe intel_gpu_top itself could 
generate the gnuplot control file when it exits, if given e.g. --gnuplot 
argument?


	- Eero

> 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);
>   	}
> 


[-- Attachment #2: intel_gpu_top_plot.sh.txt --]
[-- Type: application/x-shellscript, Size: 1834 bytes --]

[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
@ 2019-02-08 13:58   ` Eero Tamminen
  0 siblings, 0 replies; 20+ messages in thread
From: Eero Tamminen @ 2019-02-08 13:58 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, 3.14pi, Tvrtko Ursulin

[-- Attachment #1: Type: text/plain, Size: 27819 bytes --]

Hi,

On 8.2.2019 14.03, Tvrtko Ursulin wrote:
> 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

Looks nice!

If you add '#' to the start of the header lines, one could use something 
like the attached shell script to convert the saved output to SVG graphs 
with GnuPlot.

Before including the script to igt, it would need to be modified to 
adapt to the number of engines, but maybe intel_gpu_top itself could 
generate the gnuplot control file when it exits, if given e.g. --gnuplot 
argument?


	- Eero

> 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);
>   	}
> 


[-- Attachment #2: intel_gpu_top_plot.sh.txt --]
[-- Type: application/x-shellscript, Size: 1834 bytes --]

[-- Attachment #3: Type: text/plain, Size: 154 bytes --]

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 20+ 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-08 12:03 ` [igt-dev] " Tvrtko Ursulin
                   ` (4 preceding siblings ...)
  (?)
@ 2019-02-08 14:11 ` Patchwork
  -1 siblings, 0 replies; 20+ messages in thread
From: Patchwork @ 2019-02-08 14:11 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/56393/
State : success

== Summary ==

CI Bug Log - changes from IGT_4814_full -> IGTPW_2362_full
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/56393/revisions/1/mbox/

Known issues
------------

  Here are the changes found in IGTPW_2362_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_mmap_gtt@hang:
    - shard-snb:          NOTRUN -> FAIL [fdo#109469]

  * igt@kms_available_modes_crc@available_mode_test_crc:
    - shard-kbl:          PASS -> FAIL [fdo#106641]

  * igt@kms_busy@extended-modeset-hang-newfb-with-reset-render-a:
    - shard-snb:          NOTRUN -> DMESG-WARN [fdo#107956]

  * igt@kms_color@pipe-a-ctm-max:
    - shard-apl:          PASS -> FAIL [fdo#108147]

  * igt@kms_color@pipe-a-gamma:
    - shard-kbl:          PASS -> FAIL [fdo#104782]

  * igt@kms_cursor_crc@cursor-128x128-suspend:
    - shard-apl:          PASS -> FAIL [fdo#103191] / [fdo#103232]

  * igt@kms_cursor_crc@cursor-64x21-sliding:
    - shard-apl:          PASS -> FAIL [fdo#103232] +1
    - shard-glk:          PASS -> FAIL [fdo#103232]

  * igt@kms_flip@2x-flip-vs-expired-vblank-interruptible:
    - shard-glk:          PASS -> FAIL [fdo#105363]

  * igt@kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-mmap-gtt:
    - shard-kbl:          PASS -> DMESG-WARN [fdo#103558] / [fdo#105602] +6

  * igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-blt:
    - shard-kbl:          PASS -> FAIL [fdo#103167]

  * igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-onoff:
    - shard-apl:          PASS -> FAIL [fdo#103167] +2

  * igt@kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-mmap-wc:
    - shard-glk:          PASS -> FAIL [fdo#103167] +9

  * igt@kms_plane@plane-position-covered-pipe-a-planes:
    - shard-glk:          PASS -> FAIL [fdo#103166] +3

  * igt@kms_plane_alpha_blend@pipe-a-alpha-opaque-fb:
    - shard-glk:          NOTRUN -> FAIL [fdo#108145]

  * igt@kms_plane_lowres@pipe-a-tiling-none:
    - shard-kbl:          PASS -> FAIL [fdo#103166]

  * igt@kms_plane_multiple@atomic-pipe-b-tiling-x:
    - shard-apl:          PASS -> FAIL [fdo#103166] +2

  * igt@kms_rotation_crc@multiplane-rotation:
    - shard-kbl:          PASS -> FAIL [fdo#109016]

  * igt@kms_setmode@basic:
    - shard-apl:          PASS -> FAIL [fdo#99912]

  
#### Possible fixes ####

  * igt@kms_ccs@pipe-a-crc-sprite-planes-basic:
    - shard-kbl:          FAIL [fdo#107725] / [fdo#108145] -> PASS

  * igt@kms_color@pipe-c-ctm-max:
    - shard-kbl:          FAIL [fdo#108147] -> PASS
    - shard-apl:          FAIL [fdo#108147] -> PASS

  * igt@kms_color@pipe-c-legacy-gamma:
    - shard-apl:          FAIL [fdo#104782] -> PASS

  * igt@kms_cursor_crc@cursor-128x128-random:
    - shard-apl:          FAIL [fdo#103232] -> PASS +6

  * igt@kms_cursor_crc@cursor-256x256-random:
    - shard-kbl:          FAIL [fdo#103232] -> PASS

  * igt@kms_cursor_crc@cursor-256x85-sliding:
    - shard-glk:          FAIL [fdo#103232] -> PASS +1

  * igt@kms_flip@flip-vs-expired-vblank:
    - shard-glk:          FAIL [fdo#102887] / [fdo#105363] -> PASS

  * igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-mmap-gtt:
    - shard-apl:          FAIL [fdo#103167] -> PASS +2

  * igt@kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-render:
    - shard-glk:          FAIL [fdo#103167] -> PASS

  * igt@kms_plane@pixel-format-pipe-a-planes-source-clamping:
    - shard-glk:          FAIL [fdo#108948] -> PASS

  * igt@kms_plane@pixel-format-pipe-b-planes-source-clamping:
    - shard-apl:          FAIL [fdo#108948] -> PASS

  * igt@kms_plane@plane-position-covered-pipe-c-planes:
    - shard-apl:          FAIL [fdo#103166] -> PASS +2

  * igt@kms_plane_alpha_blend@pipe-b-alpha-opaque-fb:
    - shard-glk:          FAIL [fdo#108145] -> PASS +1

  * igt@kms_plane_multiple@atomic-pipe-c-tiling-yf:
    - shard-kbl:          FAIL [fdo#103166] -> PASS +4

  * igt@kms_setmode@basic:
    - shard-kbl:          FAIL [fdo#99912] -> PASS

  * igt@kms_vblank@pipe-c-ts-continuation-suspend:
    - shard-kbl:          INCOMPLETE [fdo#103665] -> PASS

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#102887]: https://bugs.freedesktop.org/show_bug.cgi?id=102887
  [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#103558]: https://bugs.freedesktop.org/show_bug.cgi?id=103558
  [fdo#103665]: https://bugs.freedesktop.org/show_bug.cgi?id=103665
  [fdo#104782]: https://bugs.freedesktop.org/show_bug.cgi?id=104782
  [fdo#105363]: https://bugs.freedesktop.org/show_bug.cgi?id=105363
  [fdo#105602]: https://bugs.freedesktop.org/show_bug.cgi?id=105602
  [fdo#106641]: https://bugs.freedesktop.org/show_bug.cgi?id=106641
  [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#108147]: https://bugs.freedesktop.org/show_bug.cgi?id=108147
  [fdo#108948]: https://bugs.freedesktop.org/show_bug.cgi?id=108948
  [fdo#109016]: https://bugs.freedesktop.org/show_bug.cgi?id=109016
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109278]: https://bugs.freedesktop.org/show_bug.cgi?id=109278
  [fdo#109469]: https://bugs.freedesktop.org/show_bug.cgi?id=109469
  [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_4814 -> IGTPW_2362

  CI_DRM_5566: dc8d2739325432dd969028550e9e9b3070b76db1 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_2362: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2362/
  IGT_4814: cec046d72944a2d559ce5bd83358f08f20454005 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2362/
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
  2019-02-08 13:58   ` [igt-dev] " Eero Tamminen
@ 2019-02-11  9:49     ` Tvrtko Ursulin
  -1 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2019-02-11  9:49 UTC (permalink / raw)
  To: Eero Tamminen, igt-dev; +Cc: Intel-gfx, 3.14pi


On 08/02/2019 13:58, Eero Tamminen wrote:
> Hi,
> 
> On 8.2.2019 14.03, Tvrtko Ursulin wrote:
>> 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
> 
> Looks nice!
> 
> If you add '#' to the start of the header lines, one could use something 
> like the attached shell script to convert the saved output to SVG graphs 
> with GnuPlot.
> 
> Before including the script to igt, it would need to be modified to 
> adapt to the number of engines, but maybe intel_gpu_top itself could 
> generate the gnuplot control file when it exits, if given e.g. --gnuplot 
> argument?

Hello feature creep! :D

I could add gnuplot output mode later, just to keep this stage simpler. 
In that case I would prefer that this mode outputs a single file (or 
stdout stream) which could be fed to gnuplot directly. Eg. intel_gpu_top 
-g -o file.out && gnuplot file.out, or even, intel_gpu_top -g | gnuplot. 
Quick googling shows it should be possible to embed the data in the 
"control file", I am only not sure about the output filename in the 
second example. But anyway, first example should definitely 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] 20+ messages in thread

* Re: [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
@ 2019-02-11  9:49     ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2019-02-11  9:49 UTC (permalink / raw)
  To: Eero Tamminen, igt-dev; +Cc: Intel-gfx, 3.14pi, Tvrtko Ursulin


On 08/02/2019 13:58, Eero Tamminen wrote:
> Hi,
> 
> On 8.2.2019 14.03, Tvrtko Ursulin wrote:
>> 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
> 
> Looks nice!
> 
> If you add '#' to the start of the header lines, one could use something 
> like the attached shell script to convert the saved output to SVG graphs 
> with GnuPlot.
> 
> Before including the script to igt, it would need to be modified to 
> adapt to the number of engines, but maybe intel_gpu_top itself could 
> generate the gnuplot control file when it exits, if given e.g. --gnuplot 
> argument?

Hello feature creep! :D

I could add gnuplot output mode later, just to keep this stage simpler. 
In that case I would prefer that this mode outputs a single file (or 
stdout stream) which could be fed to gnuplot directly. Eg. intel_gpu_top 
-g -o file.out && gnuplot file.out, or even, intel_gpu_top -g | gnuplot. 
Quick googling shows it should be possible to embed the data in the 
"control file", I am only not sure about the output filename in the 
second example. But anyway, first example should definitely work.

Regards,

Tvrtko


_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
  2019-02-11  9:49     ` [igt-dev] " Tvrtko Ursulin
@ 2019-02-12  9:31       ` Eero Tamminen via igt-dev
  -1 siblings, 0 replies; 20+ messages in thread
From: Eero Tamminen @ 2019-02-12  9:31 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, 3.14pi

Hi,

On 11.2.2019 11.49, Tvrtko Ursulin wrote:
> On 08/02/2019 13:58, Eero Tamminen wrote:
>> On 8.2.2019 14.03, Tvrtko Ursulin wrote:
>>> 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
>>
>> Looks nice!
>>
>> If you add '#' to the start of the header lines, one could use 
>> something like the attached shell script to convert the saved output 
>> to SVG graphs with GnuPlot.
>>
>> Before including the script to igt, it would need to be modified to 
>> adapt to the number of engines, but maybe intel_gpu_top itself could 
>> generate the gnuplot control file when it exits, if given e.g. 
>> --gnuplot argument?
> 
> Hello feature creep! :D
> 
> I could add gnuplot output mode later, just to keep this stage simpler. 
> In that case I would prefer that this mode outputs a single file (or 
> stdout stream) which could be fed to gnuplot directly. Eg. intel_gpu_top 
> -g -o file.out && gnuplot file.out, or even, intel_gpu_top -g | gnuplot. 
> Quick googling shows it should be possible to embed the data in the 
> "control file",

I didn't know that, but it seems simple.  One just uses variable $name 
for the data part instead of headers, and uses that variable name 
instead of file name in plot commands after the data.

New enough GnuPlot version is in all distros which have new enough kernel.


> I am only not sure about the output filename in the 
> second example. But anyway, first example should definitely work.

Gnuplot doesn't care about the control file name extension, and it can 
read it from stdin.  Both of these seem to work fine:
	gnuplot - < plot.gnu
	gnuplot < plot.gnu


	- Eero
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
@ 2019-02-12  9:31       ` Eero Tamminen via igt-dev
  0 siblings, 0 replies; 20+ messages in thread
From: Eero Tamminen via igt-dev @ 2019-02-12  9:31 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, 3.14pi, Tvrtko Ursulin

Hi,

On 11.2.2019 11.49, Tvrtko Ursulin wrote:
> On 08/02/2019 13:58, Eero Tamminen wrote:
>> On 8.2.2019 14.03, Tvrtko Ursulin wrote:
>>> 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
>>
>> Looks nice!
>>
>> If you add '#' to the start of the header lines, one could use 
>> something like the attached shell script to convert the saved output 
>> to SVG graphs with GnuPlot.
>>
>> Before including the script to igt, it would need to be modified to 
>> adapt to the number of engines, but maybe intel_gpu_top itself could 
>> generate the gnuplot control file when it exits, if given e.g. 
>> --gnuplot argument?
> 
> Hello feature creep! :D
> 
> I could add gnuplot output mode later, just to keep this stage simpler. 
> In that case I would prefer that this mode outputs a single file (or 
> stdout stream) which could be fed to gnuplot directly. Eg. intel_gpu_top 
> -g -o file.out && gnuplot file.out, or even, intel_gpu_top -g | gnuplot. 
> Quick googling shows it should be possible to embed the data in the 
> "control file",

I didn't know that, but it seems simple.  One just uses variable $name 
for the data part instead of headers, and uses that variable name 
instead of file name in plot commands after the data.

New enough GnuPlot version is in all distros which have new enough kernel.


> I am only not sure about the output filename in the 
> second example. But anyway, first example should definitely work.

Gnuplot doesn't care about the control file name extension, and it can 
read it from stdin.  Both of these seem to work fine:
	gnuplot - < plot.gnu
	gnuplot < plot.gnu


	- Eero
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] ✓ Fi.CI.BAT: success for series starting with [1/2] tools/intel_gpu_top: Add support for stdout logging
  2019-02-08 12:29 ` [igt-dev] ✓ Fi.CI.BAT: success for series starting with [1/2] " Patchwork
@ 2019-02-25 10:04   ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2019-02-25 10:04 UTC (permalink / raw)
  To: igt-dev


On 08/02/2019 12:29, Patchwork wrote:
> == Series Details ==
> 
> Series: series starting with [1/2] tools/intel_gpu_top: Add support for stdout logging
> URL   : https://patchwork.freedesktop.org/series/56393/
> State : success
> 
> == Summary ==
> 
> CI Bug Log - changes from IGT_4814 -> IGTPW_2362
> ====================================================
> 
> Summary
> -------
> 
>    **SUCCESS**
> 
>    No regressions found.
> 
>    External URL: https://patchwork.freedesktop.org/api/1.0/series/56393/revisions/1/mbox/
> 
> Known issues
> ------------
> 
>    Here are the changes found in IGTPW_2362 that come from known issues:
> 
> ### IGT changes ###
> 
> #### Issues hit ####
> 
>    * igt@i915_selftest@live_evict:
>      - fi-bsw-kefka:       PASS -> DMESG-WARN [fdo#107709]
> 
>    * igt@kms_chamelium@hdmi-hpd-fast:
>      - fi-kbl-7500u:       PASS -> FAIL [fdo#109485]
> 
>    
>    {name}: This element is suppressed. This means it is ignored when computing
>            the status of the difference (SUCCESS, WARNING, or FAILURE).
> 
>    [fdo#107709]: https://bugs.freedesktop.org/show_bug.cgi?id=107709
>    [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
>    [fdo#109527]: https://bugs.freedesktop.org/show_bug.cgi?id=109527
> 
> 
> Participating hosts (45 -> 42)
> ------------------------------
> 
>    Additional (3): fi-hsw-4770r fi-byt-j1900 fi-gdg-551
>    Missing    (6): fi-kbl-soraka fi-ilk-m540 fi-byt-squawks fi-bsw-cyan fi-kbl-x1275 fi-icl-y
> 
> 
> Build changes
> -------------
> 
>      * IGT: IGT_4814 -> IGTPW_2362
> 
>    CI_DRM_5566: dc8d2739325432dd969028550e9e9b3070b76db1 @ git://anongit.freedesktop.org/gfx-ci/linux
>    IGTPW_2362: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2362/
>    IGT_4814: cec046d72944a2d559ce5bd83358f08f20454005 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
> 
> == Logs ==
> 
> For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2362/
> 

I've pushed this in the absence of any feedback from the original 
reporter/requester - given that internally we were happy and thought it 
was an improvement. Case closed for now.

Regards,

Tvrtko
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
  2019-02-11 12:21 ` Chris Wilson
@ 2019-02-11 17:08   ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ 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] 20+ messages in thread

* Re: [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
  2019-02-11 11:45 [IGT 1/2] " Tvrtko Ursulin
@ 2019-02-11 12:21 ` Chris Wilson
  2019-02-11 17:08   ` Tvrtko Ursulin
  0 siblings, 1 reply; 20+ 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

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
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging
@ 2019-02-11 11:45 Tvrtko Ursulin
  2019-02-11 12:21 ` Chris Wilson
  0 siblings, 1 reply; 20+ messages in thread
From: Tvrtko Ursulin @ 2019-02-11 11:45 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Eero Tamminen, 3.14pi

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

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2019-02-25 10:04 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-08 12:03 [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging Tvrtko Ursulin
2019-02-08 12:03 ` [igt-dev] " Tvrtko Ursulin
2019-02-08 12:03 ` [IGT 2/2] tools/intel_gpu_top: Add file output capability Tvrtko Ursulin
2019-02-08 12:03   ` [igt-dev] " Tvrtko Ursulin
2019-02-08 12:14   ` Chris Wilson
2019-02-08 12:14     ` [igt-dev] " Chris Wilson
2019-02-08 12:10 ` [IGT 1/2] tools/intel_gpu_top: Add support for stdout logging Chris Wilson
2019-02-08 12:10   ` [igt-dev] " Chris Wilson
2019-02-08 12:29 ` [igt-dev] ✓ Fi.CI.BAT: success for series starting with [1/2] " Patchwork
2019-02-25 10:04   ` Tvrtko Ursulin
2019-02-08 13:58 ` [IGT 1/2] " Eero Tamminen
2019-02-08 13:58   ` [igt-dev] " Eero Tamminen
2019-02-11  9:49   ` Tvrtko Ursulin
2019-02-11  9:49     ` [igt-dev] " Tvrtko Ursulin
2019-02-12  9:31     ` Eero Tamminen
2019-02-12  9:31       ` [igt-dev] " Eero Tamminen via igt-dev
2019-02-08 14:11 ` [igt-dev] ✓ Fi.CI.IGT: success for series starting with [1/2] " Patchwork
2019-02-11 11:45 [IGT 1/2] " Tvrtko Ursulin
2019-02-11 12:21 ` Chris Wilson
2019-02-11 17:08   ` Tvrtko Ursulin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.