All of lore.kernel.org
 help / color / mirror / Atom feed
* [Intel-gfx] [PATCH i-g-t 1/3] intel_gpu_top: Wrap interactive header
@ 2021-02-10  9:37 ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10  9:37 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Slight improvement with regards to wrapping header components to fit
console width. If a single element is wider than max it can still
overflow but it should now work better for practical console widths.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tools/intel_gpu_top.c | 81 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 68 insertions(+), 13 deletions(-)

diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 21084185d896..63ef77056341 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -1461,6 +1461,41 @@ static bool print_groups(struct cnt_group **groups)
 	return print_data;
 }
 
+static int __attribute__ ((format(__printf__, 6, 7)))
+print_header_token(const char *cont, int lines, int con_w, int con_h, int *rem,
+		   const char *fmt, ...)
+{
+	const char *indent = "\n   ";
+	char buf[256];
+	va_list args;
+	int ret;
+
+	if (lines >= con_h)
+		return lines;
+
+	va_start(args, fmt);
+	ret = vsnprintf(buf, sizeof(buf), fmt, args);
+	assert(ret < sizeof(buf));
+	va_end(args);
+
+	ret = (cont ? strlen(cont) : 0) + strlen(buf);
+	*rem -= ret;
+	if (*rem < 0) {
+		if (++lines >= con_h)
+			return lines;
+
+		*rem = con_w - ret - strlen(indent);
+		cont = indent;
+	}
+
+	if (cont)
+		ret = printf("%s%s", cont, buf);
+	else
+		ret = printf("%s", buf);
+
+	return lines;
+}
+
 static const char *header_msg;
 
 static int
@@ -1540,23 +1575,43 @@ print_header(const struct igt_device_card *card,
 	*consumed = print_groups(groups);
 
 	if (output_mode == INTERACTIVE) {
+		int rem = con_w;
+
 		printf("\033[H\033[J");
 
-		if (lines++ < con_h)
-			printf("intel-gpu-top: %s @ %s\n",
-			       codename, card->card);
-		if (lines++ < con_h) {
-			printf("%s/%s MHz; %s%% RC6; ",
-			       freq_items[1].buf, freq_items[0].buf,
-			       rc6_items[0].buf);
-			if (engines->r_gpu.present) {
-				printf("%s/%s W; ",
-				       power_items[0].buf,
-				       power_items[1].buf);
-			}
-			printf("%s irqs/s\n", irq_items[0].buf);
+		lines = print_header_token(NULL, lines, con_w, con_h, &rem,
+					   "intel-gpu-top:");
+
+		lines = print_header_token(" ", lines, con_w, con_h, &rem,
+					   "%s", codename);
+
+		lines = print_header_token(" @ ", lines, con_w, con_h, &rem,
+					   "%s", card->card);
+
+		lines = print_header_token(" - ", lines, con_w, con_h, &rem,
+					   "%s/%s MHz",
+					   freq_items[1].buf,
+					   freq_items[0].buf);
+
+		lines = print_header_token("; ", lines, con_w, con_h, &rem,
+					   "%s%% RC6",
+					   rc6_items[0].buf);
+
+		if (engines->r_gpu.present) {
+			lines = print_header_token("; ", lines, con_w, con_h,
+						   &rem,
+						   "%s/%s W",
+						   power_items[0].buf,
+						   power_items[1].buf);
 		}
 
+		lines = print_header_token("; ", lines, con_w, con_h, &rem,
+					   "%s irqs/s",
+					   irq_items[0].buf);
+
+		if (lines++ < con_h)
+			printf("\n");
+
 		if (lines++ < con_h) {
 			if (header_msg) {
 				printf(" >>> %s\n", header_msg);
-- 
2.27.0

_______________________________________________
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] [PATCH i-g-t 1/3] intel_gpu_top: Wrap interactive header
@ 2021-02-10  9:37 ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10  9:37 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Slight improvement with regards to wrapping header components to fit
console width. If a single element is wider than max it can still
overflow but it should now work better for practical console widths.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tools/intel_gpu_top.c | 81 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 68 insertions(+), 13 deletions(-)

diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 21084185d896..63ef77056341 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -1461,6 +1461,41 @@ static bool print_groups(struct cnt_group **groups)
 	return print_data;
 }
 
+static int __attribute__ ((format(__printf__, 6, 7)))
+print_header_token(const char *cont, int lines, int con_w, int con_h, int *rem,
+		   const char *fmt, ...)
+{
+	const char *indent = "\n   ";
+	char buf[256];
+	va_list args;
+	int ret;
+
+	if (lines >= con_h)
+		return lines;
+
+	va_start(args, fmt);
+	ret = vsnprintf(buf, sizeof(buf), fmt, args);
+	assert(ret < sizeof(buf));
+	va_end(args);
+
+	ret = (cont ? strlen(cont) : 0) + strlen(buf);
+	*rem -= ret;
+	if (*rem < 0) {
+		if (++lines >= con_h)
+			return lines;
+
+		*rem = con_w - ret - strlen(indent);
+		cont = indent;
+	}
+
+	if (cont)
+		ret = printf("%s%s", cont, buf);
+	else
+		ret = printf("%s", buf);
+
+	return lines;
+}
+
 static const char *header_msg;
 
 static int
@@ -1540,23 +1575,43 @@ print_header(const struct igt_device_card *card,
 	*consumed = print_groups(groups);
 
 	if (output_mode == INTERACTIVE) {
+		int rem = con_w;
+
 		printf("\033[H\033[J");
 
-		if (lines++ < con_h)
-			printf("intel-gpu-top: %s @ %s\n",
-			       codename, card->card);
-		if (lines++ < con_h) {
-			printf("%s/%s MHz; %s%% RC6; ",
-			       freq_items[1].buf, freq_items[0].buf,
-			       rc6_items[0].buf);
-			if (engines->r_gpu.present) {
-				printf("%s/%s W; ",
-				       power_items[0].buf,
-				       power_items[1].buf);
-			}
-			printf("%s irqs/s\n", irq_items[0].buf);
+		lines = print_header_token(NULL, lines, con_w, con_h, &rem,
+					   "intel-gpu-top:");
+
+		lines = print_header_token(" ", lines, con_w, con_h, &rem,
+					   "%s", codename);
+
+		lines = print_header_token(" @ ", lines, con_w, con_h, &rem,
+					   "%s", card->card);
+
+		lines = print_header_token(" - ", lines, con_w, con_h, &rem,
+					   "%s/%s MHz",
+					   freq_items[1].buf,
+					   freq_items[0].buf);
+
+		lines = print_header_token("; ", lines, con_w, con_h, &rem,
+					   "%s%% RC6",
+					   rc6_items[0].buf);
+
+		if (engines->r_gpu.present) {
+			lines = print_header_token("; ", lines, con_w, con_h,
+						   &rem,
+						   "%s/%s W",
+						   power_items[0].buf,
+						   power_items[1].buf);
 		}
 
+		lines = print_header_token("; ", lines, con_w, con_h, &rem,
+					   "%s irqs/s",
+					   irq_items[0].buf);
+
+		if (lines++ < con_h)
+			printf("\n");
+
 		if (lines++ < con_h) {
 			if (header_msg) {
 				printf(" >>> %s\n", header_msg);
-- 
2.27.0

_______________________________________________
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

* [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
  2021-02-10  9:37 ` [igt-dev] " Tvrtko Ursulin
  (?)
@ 2021-02-10  9:37 ` Tvrtko Ursulin
  2021-02-10  9:44   ` Tvrtko Ursulin
                     ` (3 more replies)
  -1 siblings, 4 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10  9:37 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Implement a default view where clients are aggregated by their PID.

Toggled by pressing 'H' similar to top(1).

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 man/intel_gpu_top.rst |   1 +
 tools/intel_gpu_top.c | 109 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 97 insertions(+), 13 deletions(-)

diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index b145d85c0440..20658e291db0 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -58,6 +58,7 @@ Supported keys:
     'n'    Toggle display of numeric client busyness overlay.
     's'    Toggle between sort modes (runtime, total runtime, pid, client id).
     'i'    Toggle display of clients which used no GPU time.
+    'H'    Toggle between per PID aggregation and individual clients.
 
 DEVICE SELECTION
 ================
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 63ef77056341..018e28a66c10 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -979,17 +979,18 @@ static int client_pid_cmp(const void *_a, const void *_b)
 
 static int (*client_cmp)(const void *, const void *) = client_last_cmp;
 
-static void sort_clients(struct clients *clients)
+static struct clients *sort_clients(struct clients *clients,
+				    int (*cmp)(const void *, const void *))
 {
 	unsigned int active, free;
 	struct client *c;
 	int tmp;
 
 	if (!clients)
-		return;
+		return clients;
 
 	qsort(clients->client, clients->num_clients, sizeof(*clients->client),
-	      client_cmp);
+	      cmp);
 
 	/* Trim excessive array space. */
 	active = 0;
@@ -1011,9 +1012,76 @@ static void sort_clients(struct clients *clients)
 						  sizeof(*c));
 		}
 	}
+
+	return clients;
+}
+
+static struct clients *aggregated_clients(struct clients *clients)
+{
+	struct client *ac, *c, *cp = NULL;
+	struct clients *aggregated;
+	int tmp, num = 0;
+
+	/* Sort by pid first to make it easy to aggregate while walking. */
+	sort_clients(clients, client_pid_cmp);
+
+	aggregated = calloc(1, sizeof(*clients));
+	assert(aggregated);
+
+	ac = calloc(clients->num_clients, sizeof(*c));
+	assert(ac);
+
+	aggregated->num_classes = clients->num_classes;
+	aggregated->class = clients->class;
+	aggregated->client = ac;
+
+	for_each_client(clients, c, tmp) {
+		unsigned int i;
+
+		if (c->status == FREE)
+			break;
+
+		assert(c->status == ALIVE);
+
+		if ((cp && c->pid != cp->pid) || !cp) {
+			ac = &aggregated->client[num];
+
+			/* New pid. */
+			ac->clients = aggregated;
+			ac->status = ALIVE;
+			ac->id = ++num;
+			ac->pid = c->pid;
+			strcpy(ac->name, c->name);
+			strcpy(ac->print_name, c->print_name);
+			ac->engines = c->engines;
+			ac->val = calloc(clients->num_classes,
+					 sizeof(ac->val[0]));
+			assert(ac->val);
+			ac->samples = 1;
+		}
+
+		cp = c;
+
+		if (c->samples < 2)
+			continue;
+
+		ac->samples = 2; /* All what matters for display. */
+		ac->total_runtime += c->total_runtime;
+		ac->last_runtime += c->last_runtime;
+
+		for (i = 0; i < clients->num_classes; i++)
+			ac->val[i] += c->val[i];
+	}
+
+	aggregated->num_clients = num;
+	aggregated->active_clients = num;
+
+	return sort_clients(aggregated, client_cmp);
 }
 
-static void scan_clients(struct clients *clients)
+static bool aggregate_pids = true;
+
+static struct clients *scan_clients(struct clients *clients)
 {
 	struct dirent *dent;
 	struct client *c;
@@ -1022,7 +1090,7 @@ static void scan_clients(struct clients *clients)
 	DIR *d;
 
 	if (!clients)
-		return;
+		return clients;
 
 	for_each_client(clients, c, tmp) {
 		assert(c->status != PROBE);
@@ -1034,7 +1102,7 @@ static void scan_clients(struct clients *clients)
 
 	d = opendir(clients->sysfs_root);
 	if (!d)
-		return;
+		return clients;
 
 	while ((dent = readdir(d)) != NULL) {
 		char name[24], pid[24];
@@ -1077,7 +1145,10 @@ static void scan_clients(struct clients *clients)
 			break;
 	}
 
-	sort_clients(clients);
+	if (aggregate_pids)
+		return aggregated_clients(clients);
+	else
+		return sort_clients(clients, client_cmp);
 }
 
 static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" };
@@ -2227,6 +2298,13 @@ static void process_stdin(unsigned int timeout_us)
 		case 's':
 			select_client_sort();
 			break;
+		case 'H':
+			aggregate_pids ^= true;
+			if (aggregate_pids)
+				header_msg = "Aggregating clients.";
+			else
+				header_msg = "Showing individual clients.";
+			break;
 		};
 	}
 }
@@ -2378,6 +2456,7 @@ int main(int argc, char **argv)
 	codename = igt_device_get_pretty_name(&card, false);
 
 	while (!stop_top) {
+		struct clients *disp_clients;
 		bool consumed = false;
 		int j, lines = 0;
 		struct winsize ws;
@@ -2400,7 +2479,7 @@ int main(int argc, char **argv)
 		pmu_sample(engines);
 		t = (double)(engines->ts.cur - engines->ts.prev) / 1e9;
 
-		scan_clients(clients);
+		disp_clients = scan_clients(clients);
 
 		if (stop_top)
 			break;
@@ -2416,14 +2495,14 @@ int main(int argc, char **argv)
 
 			lines = print_engines(engines, t, lines, con_w, con_h);
 
-			if (clients) {
+			if (disp_clients) {
 				int class_w;
 
-				lines = print_clients_header(clients, lines,
+				lines = print_clients_header(disp_clients, lines,
 							     con_w, con_h,
 							     &class_w);
 
-				for_each_client(clients, c, j) {
+				for_each_client(disp_clients, c, j) {
 					assert(c->status != PROBE);
 					if (c->status != ALIVE)
 						break; /* Active clients are first in the array. */
@@ -2437,8 +2516,9 @@ int main(int argc, char **argv)
 							     &class_w);
 				}
 
-				lines = print_clients_footer(clients, t, lines,
-							     con_w, con_h);
+				lines = print_clients_footer(disp_clients, t,
+							     lines, con_w,
+							     con_h);
 			}
 
 			pops->close_struct();
@@ -2447,6 +2527,9 @@ int main(int argc, char **argv)
 		if (stop_top)
 			break;
 
+		if (disp_clients != clients)
+			free(disp_clients);
+
 		if (output_mode == INTERACTIVE)
 			process_stdin(period_us);
 		else
-- 
2.27.0

_______________________________________________
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

* [Intel-gfx] [PATCH i-g-t 3/3] intel_gpu_top: Interactive help screen
  2021-02-10  9:37 ` [igt-dev] " Tvrtko Ursulin
@ 2021-02-10  9:37   ` Tvrtko Ursulin
  -1 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10  9:37 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Show a list of supported interactive commands when pressing 'h'.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 man/intel_gpu_top.rst |  1 +
 tools/intel_gpu_top.c | 68 ++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 61 insertions(+), 8 deletions(-)

diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index 20658e291db0..f6d74852558b 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -54,6 +54,7 @@ RUNTIME CONTROL
 Supported keys:
 
     'q'    Exit from the tool.
+    'h'    Show interactive help.
     '1'    Toggle between aggregated engine class and physical engine mode.
     'n'    Toggle display of numeric client busyness overlay.
     's'    Toggle between sort modes (runtime, total runtime, pid, client id).
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 018e28a66c10..c0e45ddb24e1 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -2255,19 +2255,31 @@ static void select_client_sort(void)
 	header_msg = cmp[client_sort].msg;
 }
 
-static void process_stdin(unsigned int timeout_us)
+static bool in_help;
+
+static void process_help_stdin(void)
 {
-	struct pollfd p = { .fd = 0, .events = POLLIN };
-	int ret;
+	for (;;) {
+		int ret;
+		char c;
 
-	ret = poll(&p, 1, timeout_us / 1000);
-	if (ret <= 0) {
-		if (ret < 0)
-			stop_top = true;
-		return;
+		ret = read(0, &c, 1);
+		if (ret <= 0)
+			break;
+
+		switch (c) {
+		case 'q':
+		case 'h':
+			in_help = false;
+			break;
+		};
 	}
+}
 
+static void process_normal_stdin(void)
+{
 	for (;;) {
+		int ret;
 		char c;
 
 		ret = read(0, &c, 1);
@@ -2298,6 +2310,9 @@ static void process_stdin(unsigned int timeout_us)
 		case 's':
 			select_client_sort();
 			break;
+		case 'h':
+			in_help = true;
+			break;
 		case 'H':
 			aggregate_pids ^= true;
 			if (aggregate_pids)
@@ -2309,6 +2324,38 @@ static void process_stdin(unsigned int timeout_us)
 	}
 }
 
+static void process_stdin(unsigned int timeout_us)
+{
+	struct pollfd p = { .fd = 0, .events = POLLIN };
+	int ret;
+
+	ret = poll(&p, 1, timeout_us / 1000);
+	if (ret <= 0) {
+		if (ret < 0)
+			stop_top = true;
+		return;
+	}
+
+	if (in_help)
+		process_help_stdin();
+	else
+		process_normal_stdin();
+}
+
+static void show_help_screen(void)
+{
+	printf(
+"Help for interactive commands:\n\n"
+"    '1'    Toggle between aggregated engine class and physical engine mode.\n"
+"    'n'    Toggle display of numeric client busyness overlay.\n"
+"    's'    Toggle between sort modes (runtime, total runtime, pid, client id).\n"
+"    'i'    Toggle display of clients which used no GPU time.\n"
+"    'H'    Toggle between per PID aggregation and individual clients.\n"
+"\n"
+"    'h' or 'q'    Exit interactive help.\n"
+"\n");
+}
+
 int main(int argc, char **argv)
 {
 	unsigned int period_us = DEFAULT_PERIOD_MS * 1000;
@@ -2491,6 +2538,11 @@ int main(int argc, char **argv)
 					     t, lines, con_w, con_h,
 					     &consumed);
 
+			if (in_help) {
+				show_help_screen();
+				break;
+			}
+
 			lines = print_imc(engines, t, lines, con_w, con_h);
 
 			lines = print_engines(engines, t, lines, con_w, con_h);
-- 
2.27.0

_______________________________________________
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] [PATCH i-g-t 3/3] intel_gpu_top: Interactive help screen
@ 2021-02-10  9:37   ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10  9:37 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Show a list of supported interactive commands when pressing 'h'.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 man/intel_gpu_top.rst |  1 +
 tools/intel_gpu_top.c | 68 ++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 61 insertions(+), 8 deletions(-)

diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index 20658e291db0..f6d74852558b 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -54,6 +54,7 @@ RUNTIME CONTROL
 Supported keys:
 
     'q'    Exit from the tool.
+    'h'    Show interactive help.
     '1'    Toggle between aggregated engine class and physical engine mode.
     'n'    Toggle display of numeric client busyness overlay.
     's'    Toggle between sort modes (runtime, total runtime, pid, client id).
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 018e28a66c10..c0e45ddb24e1 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -2255,19 +2255,31 @@ static void select_client_sort(void)
 	header_msg = cmp[client_sort].msg;
 }
 
-static void process_stdin(unsigned int timeout_us)
+static bool in_help;
+
+static void process_help_stdin(void)
 {
-	struct pollfd p = { .fd = 0, .events = POLLIN };
-	int ret;
+	for (;;) {
+		int ret;
+		char c;
 
-	ret = poll(&p, 1, timeout_us / 1000);
-	if (ret <= 0) {
-		if (ret < 0)
-			stop_top = true;
-		return;
+		ret = read(0, &c, 1);
+		if (ret <= 0)
+			break;
+
+		switch (c) {
+		case 'q':
+		case 'h':
+			in_help = false;
+			break;
+		};
 	}
+}
 
+static void process_normal_stdin(void)
+{
 	for (;;) {
+		int ret;
 		char c;
 
 		ret = read(0, &c, 1);
@@ -2298,6 +2310,9 @@ static void process_stdin(unsigned int timeout_us)
 		case 's':
 			select_client_sort();
 			break;
+		case 'h':
+			in_help = true;
+			break;
 		case 'H':
 			aggregate_pids ^= true;
 			if (aggregate_pids)
@@ -2309,6 +2324,38 @@ static void process_stdin(unsigned int timeout_us)
 	}
 }
 
+static void process_stdin(unsigned int timeout_us)
+{
+	struct pollfd p = { .fd = 0, .events = POLLIN };
+	int ret;
+
+	ret = poll(&p, 1, timeout_us / 1000);
+	if (ret <= 0) {
+		if (ret < 0)
+			stop_top = true;
+		return;
+	}
+
+	if (in_help)
+		process_help_stdin();
+	else
+		process_normal_stdin();
+}
+
+static void show_help_screen(void)
+{
+	printf(
+"Help for interactive commands:\n\n"
+"    '1'    Toggle between aggregated engine class and physical engine mode.\n"
+"    'n'    Toggle display of numeric client busyness overlay.\n"
+"    's'    Toggle between sort modes (runtime, total runtime, pid, client id).\n"
+"    'i'    Toggle display of clients which used no GPU time.\n"
+"    'H'    Toggle between per PID aggregation and individual clients.\n"
+"\n"
+"    'h' or 'q'    Exit interactive help.\n"
+"\n");
+}
+
 int main(int argc, char **argv)
 {
 	unsigned int period_us = DEFAULT_PERIOD_MS * 1000;
@@ -2491,6 +2538,11 @@ int main(int argc, char **argv)
 					     t, lines, con_w, con_h,
 					     &consumed);
 
+			if (in_help) {
+				show_help_screen();
+				break;
+			}
+
 			lines = print_imc(engines, t, lines, con_w, con_h);
 
 			lines = print_engines(engines, t, lines, con_w, con_h);
-- 
2.27.0

_______________________________________________
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

* [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
  2021-02-10  9:37 ` [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default Tvrtko Ursulin
@ 2021-02-10  9:44   ` Tvrtko Ursulin
  2021-02-10 10:35     ` Chris Wilson
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10  9:44 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Implement a default view where clients are aggregated by their PID.

Toggled by pressing 'H' similar to top(1).

v2:
 * Fix memory leak.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 man/intel_gpu_top.rst |   1 +
 tools/intel_gpu_top.c | 121 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 109 insertions(+), 13 deletions(-)

diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index b145d85c0440..20658e291db0 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -58,6 +58,7 @@ Supported keys:
     'n'    Toggle display of numeric client busyness overlay.
     's'    Toggle between sort modes (runtime, total runtime, pid, client id).
     'i'    Toggle display of clients which used no GPU time.
+    'H'    Toggle between per PID aggregation and individual clients.
 
 DEVICE SELECTION
 ================
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 63ef77056341..525fb2cd539b 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -979,17 +979,18 @@ static int client_pid_cmp(const void *_a, const void *_b)
 
 static int (*client_cmp)(const void *, const void *) = client_last_cmp;
 
-static void sort_clients(struct clients *clients)
+static struct clients *sort_clients(struct clients *clients,
+				    int (*cmp)(const void *, const void *))
 {
 	unsigned int active, free;
 	struct client *c;
 	int tmp;
 
 	if (!clients)
-		return;
+		return clients;
 
 	qsort(clients->client, clients->num_clients, sizeof(*clients->client),
-	      client_cmp);
+	      cmp);
 
 	/* Trim excessive array space. */
 	active = 0;
@@ -1011,9 +1012,88 @@ static void sort_clients(struct clients *clients)
 						  sizeof(*c));
 		}
 	}
+
+	return clients;
+}
+
+static struct clients *aggregated_clients(struct clients *clients)
+{
+	struct client *ac, *c, *cp = NULL;
+	struct clients *aggregated;
+	int tmp, num = 0;
+
+	/* Sort by pid first to make it easy to aggregate while walking. */
+	sort_clients(clients, client_pid_cmp);
+
+	aggregated = calloc(1, sizeof(*clients));
+	assert(aggregated);
+
+	ac = calloc(clients->num_clients, sizeof(*c));
+	assert(ac);
+
+	aggregated->num_classes = clients->num_classes;
+	aggregated->class = clients->class;
+	aggregated->client = ac;
+
+	for_each_client(clients, c, tmp) {
+		unsigned int i;
+
+		if (c->status == FREE)
+			break;
+
+		assert(c->status == ALIVE);
+
+		if ((cp && c->pid != cp->pid) || !cp) {
+			ac = &aggregated->client[num];
+
+			/* New pid. */
+			ac->clients = aggregated;
+			ac->status = ALIVE;
+			ac->id = ++num;
+			ac->pid = c->pid;
+			strcpy(ac->name, c->name);
+			strcpy(ac->print_name, c->print_name);
+			ac->engines = c->engines;
+			ac->val = calloc(clients->num_classes,
+					 sizeof(ac->val[0]));
+			assert(ac->val);
+			ac->samples = 1;
+		}
+
+		cp = c;
+
+		if (c->samples < 2)
+			continue;
+
+		ac->samples = 2; /* All what matters for display. */
+		ac->total_runtime += c->total_runtime;
+		ac->last_runtime += c->last_runtime;
+
+		for (i = 0; i < clients->num_classes; i++)
+			ac->val[i] += c->val[i];
+	}
+
+	aggregated->num_clients = num;
+	aggregated->active_clients = num;
+
+	return sort_clients(aggregated, client_cmp);
 }
 
-static void scan_clients(struct clients *clients)
+static void free_clients(struct clients *clients)
+{
+	struct client *c;
+	unsigned int tmp;
+
+	for_each_client(clients, c, tmp)
+		free(c->val);
+
+	free(clients->client);
+	free(clients);
+}
+
+static bool aggregate_pids = true;
+
+static struct clients *scan_clients(struct clients *clients)
 {
 	struct dirent *dent;
 	struct client *c;
@@ -1022,7 +1102,7 @@ static void scan_clients(struct clients *clients)
 	DIR *d;
 
 	if (!clients)
-		return;
+		return clients;
 
 	for_each_client(clients, c, tmp) {
 		assert(c->status != PROBE);
@@ -1034,7 +1114,7 @@ static void scan_clients(struct clients *clients)
 
 	d = opendir(clients->sysfs_root);
 	if (!d)
-		return;
+		return clients;
 
 	while ((dent = readdir(d)) != NULL) {
 		char name[24], pid[24];
@@ -1077,7 +1157,10 @@ static void scan_clients(struct clients *clients)
 			break;
 	}
 
-	sort_clients(clients);
+	if (aggregate_pids)
+		return aggregated_clients(clients);
+	else
+		return sort_clients(clients, client_cmp);
 }
 
 static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" };
@@ -2227,6 +2310,13 @@ static void process_stdin(unsigned int timeout_us)
 		case 's':
 			select_client_sort();
 			break;
+		case 'H':
+			aggregate_pids ^= true;
+			if (aggregate_pids)
+				header_msg = "Aggregating clients.";
+			else
+				header_msg = "Showing individual clients.";
+			break;
 		};
 	}
 }
@@ -2378,6 +2468,7 @@ int main(int argc, char **argv)
 	codename = igt_device_get_pretty_name(&card, false);
 
 	while (!stop_top) {
+		struct clients *disp_clients;
 		bool consumed = false;
 		int j, lines = 0;
 		struct winsize ws;
@@ -2400,7 +2491,7 @@ int main(int argc, char **argv)
 		pmu_sample(engines);
 		t = (double)(engines->ts.cur - engines->ts.prev) / 1e9;
 
-		scan_clients(clients);
+		disp_clients = scan_clients(clients);
 
 		if (stop_top)
 			break;
@@ -2416,14 +2507,14 @@ int main(int argc, char **argv)
 
 			lines = print_engines(engines, t, lines, con_w, con_h);
 
-			if (clients) {
+			if (disp_clients) {
 				int class_w;
 
-				lines = print_clients_header(clients, lines,
+				lines = print_clients_header(disp_clients, lines,
 							     con_w, con_h,
 							     &class_w);
 
-				for_each_client(clients, c, j) {
+				for_each_client(disp_clients, c, j) {
 					assert(c->status != PROBE);
 					if (c->status != ALIVE)
 						break; /* Active clients are first in the array. */
@@ -2437,8 +2528,9 @@ int main(int argc, char **argv)
 							     &class_w);
 				}
 
-				lines = print_clients_footer(clients, t, lines,
-							     con_w, con_h);
+				lines = print_clients_footer(disp_clients, t,
+							     lines, con_w,
+							     con_h);
 			}
 
 			pops->close_struct();
@@ -2447,6 +2539,9 @@ int main(int argc, char **argv)
 		if (stop_top)
 			break;
 
+		if (disp_clients != clients)
+			free_clients(disp_clients);
+
 		if (output_mode == INTERACTIVE)
 			process_stdin(period_us);
 		else
-- 
2.27.0

_______________________________________________
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

* Re: [Intel-gfx] [igt-dev] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
  2021-02-10  9:37 ` [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default Tvrtko Ursulin
@ 2021-02-10 10:35     ` Chris Wilson
  2021-02-10 10:35     ` Chris Wilson
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2021-02-10 10:35 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2021-02-10 09:37:55)
> +static struct clients *aggregated_clients(struct clients *clients)
> +{
> +       struct client *ac, *c, *cp = NULL;
> +       struct clients *aggregated;
> +       int tmp, num = 0;
> +
> +       /* Sort by pid first to make it easy to aggregate while walking. */
> +       sort_clients(clients, client_pid_cmp);

You could eliminate this tiny bit of duplication by always calling
aggregated_clients() and returning here for !aggregate_pids.

> +       aggregated = calloc(1, sizeof(*clients));
> +       assert(aggregated);
> +
> +       ac = calloc(clients->num_clients, sizeof(*c));
> +       assert(ac);
> +
> +       aggregated->num_classes = clients->num_classes;
> +       aggregated->class = clients->class;
> +       aggregated->client = ac;
> +
> +       for_each_client(clients, c, tmp) {
> +               unsigned int i;
> +
> +               if (c->status == FREE)
> +                       break;
> +
> +               assert(c->status == ALIVE);
> +
> +               if ((cp && c->pid != cp->pid) || !cp) {
> +                       ac = &aggregated->client[num];
> +
> +                       /* New pid. */
> +                       ac->clients = aggregated;
> +                       ac->status = ALIVE;
> +                       ac->id = ++num;
> +                       ac->pid = c->pid;
> +                       strcpy(ac->name, c->name);
> +                       strcpy(ac->print_name, c->print_name);
> +                       ac->engines = c->engines;
> +                       ac->val = calloc(clients->num_classes,
> +                                        sizeof(ac->val[0]));
> +                       assert(ac->val);
> +                       ac->samples = 1;
> +               }
> +
> +               cp = c;
> +
> +               if (c->samples < 2)
> +                       continue;
> +
> +               ac->samples = 2; /* All what matters for display. */
> +               ac->total_runtime += c->total_runtime;
> +               ac->last_runtime += c->last_runtime;
> +
> +               for (i = 0; i < clients->num_classes; i++)
> +                       ac->val[i] += c->val[i];
> +       }
> +
> +       aggregated->num_clients = num;
> +       aggregated->active_clients = num;
> +
> +       return sort_clients(aggregated, client_cmp);
>  }

Ok, that works very well. Hmm. The sort order does seem a little jumpy
though. May I suggest ac->id = -c->pid; instead of num;
-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] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
@ 2021-02-10 10:35     ` Chris Wilson
  0 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2021-02-10 10:35 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2021-02-10 09:37:55)
> +static struct clients *aggregated_clients(struct clients *clients)
> +{
> +       struct client *ac, *c, *cp = NULL;
> +       struct clients *aggregated;
> +       int tmp, num = 0;
> +
> +       /* Sort by pid first to make it easy to aggregate while walking. */
> +       sort_clients(clients, client_pid_cmp);

You could eliminate this tiny bit of duplication by always calling
aggregated_clients() and returning here for !aggregate_pids.

> +       aggregated = calloc(1, sizeof(*clients));
> +       assert(aggregated);
> +
> +       ac = calloc(clients->num_clients, sizeof(*c));
> +       assert(ac);
> +
> +       aggregated->num_classes = clients->num_classes;
> +       aggregated->class = clients->class;
> +       aggregated->client = ac;
> +
> +       for_each_client(clients, c, tmp) {
> +               unsigned int i;
> +
> +               if (c->status == FREE)
> +                       break;
> +
> +               assert(c->status == ALIVE);
> +
> +               if ((cp && c->pid != cp->pid) || !cp) {
> +                       ac = &aggregated->client[num];
> +
> +                       /* New pid. */
> +                       ac->clients = aggregated;
> +                       ac->status = ALIVE;
> +                       ac->id = ++num;
> +                       ac->pid = c->pid;
> +                       strcpy(ac->name, c->name);
> +                       strcpy(ac->print_name, c->print_name);
> +                       ac->engines = c->engines;
> +                       ac->val = calloc(clients->num_classes,
> +                                        sizeof(ac->val[0]));
> +                       assert(ac->val);
> +                       ac->samples = 1;
> +               }
> +
> +               cp = c;
> +
> +               if (c->samples < 2)
> +                       continue;
> +
> +               ac->samples = 2; /* All what matters for display. */
> +               ac->total_runtime += c->total_runtime;
> +               ac->last_runtime += c->last_runtime;
> +
> +               for (i = 0; i < clients->num_classes; i++)
> +                       ac->val[i] += c->val[i];
> +       }
> +
> +       aggregated->num_clients = num;
> +       aggregated->active_clients = num;
> +
> +       return sort_clients(aggregated, client_cmp);
>  }

Ok, that works very well. Hmm. The sort order does seem a little jumpy
though. May I suggest ac->id = -c->pid; instead of num;
-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: [Intel-gfx] [igt-dev] [PATCH i-g-t 3/3] intel_gpu_top: Interactive help screen
  2021-02-10  9:37   ` [igt-dev] " Tvrtko Ursulin
@ 2021-02-10 10:36     ` Chris Wilson
  -1 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2021-02-10 10:36 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2021-02-10 09:37:56)
> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> 
> Show a list of supported interactive commands when pressing 'h'.
> 
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
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] [PATCH i-g-t 3/3] intel_gpu_top: Interactive help screen
@ 2021-02-10 10:36     ` Chris Wilson
  0 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2021-02-10 10:36 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2021-02-10 09:37:56)
> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> 
> Show a list of supported interactive commands when pressing 'h'.
> 
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
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

* Re: [Intel-gfx] [PATCH i-g-t 1/3] intel_gpu_top: Wrap interactive header
  2021-02-10  9:37 ` [igt-dev] " Tvrtko Ursulin
                   ` (2 preceding siblings ...)
  (?)
@ 2021-02-10 10:37 ` Chris Wilson
  -1 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2021-02-10 10:37 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2021-02-10 09:37:54)
> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> 
> Slight improvement with regards to wrapping header components to fit
> console width. If a single element is wider than max it can still
> overflow but it should now work better for practical console widths.

I'm not fond of this layout, but that's just a personal preference.
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

* [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
  2021-02-10  9:37 ` [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default Tvrtko Ursulin
  2021-02-10  9:44   ` Tvrtko Ursulin
  2021-02-10 10:35     ` Chris Wilson
@ 2021-02-10 10:53   ` Tvrtko Ursulin
  2021-02-10 11:06       ` [igt-dev] " Chris Wilson
  2021-02-10 11:12     ` [igt-dev] " Tvrtko Ursulin
  3 siblings, 1 reply; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10 10:53 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Implement a default view where clients are aggregated by their PID.

Toggled by pressing 'H' similar to top(1).

v2:
 * Fix memory leak.

v3:
 * Do not allow sort by client id in aggregated mode.
 * Tweak sort criteria and sorting decisions. (Chris)

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 man/intel_gpu_top.rst |   1 +
 tools/intel_gpu_top.c | 130 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 118 insertions(+), 13 deletions(-)

diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index b145d85c0440..20658e291db0 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -58,6 +58,7 @@ Supported keys:
     'n'    Toggle display of numeric client busyness overlay.
     's'    Toggle between sort modes (runtime, total runtime, pid, client id).
     'i'    Toggle display of clients which used no GPU time.
+    'H'    Toggle between per PID aggregation and individual clients.
 
 DEVICE SELECTION
 ================
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 63ef77056341..695a8cb397ae 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -979,17 +979,18 @@ static int client_pid_cmp(const void *_a, const void *_b)
 
 static int (*client_cmp)(const void *, const void *) = client_last_cmp;
 
-static void sort_clients(struct clients *clients)
+static struct clients *sort_clients(struct clients *clients,
+				    int (*cmp)(const void *, const void *))
 {
 	unsigned int active, free;
 	struct client *c;
 	int tmp;
 
 	if (!clients)
-		return;
+		return clients;
 
 	qsort(clients->client, clients->num_clients, sizeof(*clients->client),
-	      client_cmp);
+	      cmp);
 
 	/* Trim excessive array space. */
 	active = 0;
@@ -1011,9 +1012,95 @@ static void sort_clients(struct clients *clients)
 						  sizeof(*c));
 		}
 	}
+
+	return clients;
 }
 
-static void scan_clients(struct clients *clients)
+static bool aggregate_pids = true;
+
+static struct clients *display_clients(struct clients *clients)
+{
+	struct client *ac, *c, *cp = NULL;
+	struct clients *aggregated;
+	int tmp, num = 0;
+
+	if (!aggregate_pids)
+		return sort_clients(clients, client_cmp);
+
+	/* Sort by pid first to make it easy to aggregate while walking. */
+	sort_clients(clients, client_pid_cmp);
+
+	aggregated = calloc(1, sizeof(*clients));
+	assert(aggregated);
+
+	ac = calloc(clients->num_clients, sizeof(*c));
+	assert(ac);
+
+	aggregated->num_classes = clients->num_classes;
+	aggregated->class = clients->class;
+	aggregated->client = ac;
+
+	for_each_client(clients, c, tmp) {
+		unsigned int i;
+
+		if (c->status == FREE)
+			break;
+
+		assert(c->status == ALIVE);
+
+		if ((cp && c->pid != cp->pid) || !cp) {
+			ac = &aggregated->client[num++];
+
+			/* New pid. */
+			ac->clients = aggregated;
+			ac->status = ALIVE;
+			ac->id = -c->pid;
+			ac->pid = c->pid;
+			ac->busy_root = -1;
+			ac->sysfs_root = -1;
+			strcpy(ac->name, c->name);
+			strcpy(ac->print_name, c->print_name);
+			ac->engines = c->engines;
+			ac->val = calloc(clients->num_classes,
+					 sizeof(ac->val[0]));
+			assert(ac->val);
+			ac->samples = 1;
+		}
+
+		cp = c;
+
+		if (c->samples < 2)
+			continue;
+
+		ac->samples = 2; /* All what matters for display. */
+		ac->total_runtime += c->total_runtime;
+		ac->last_runtime += c->last_runtime;
+
+		for (i = 0; i < clients->num_classes; i++)
+			ac->val[i] += c->val[i];
+	}
+
+	aggregated->num_clients = num;
+	aggregated->active_clients = num;
+
+	return sort_clients(aggregated, client_cmp);
+}
+
+static void free_clients(struct clients *clients)
+{
+	struct client *c;
+	unsigned int tmp;
+
+	for_each_client(clients, c, tmp) {
+		free(c->val);
+		free(c->last);
+	}
+
+	free(clients->client);
+	free(clients);
+}
+
+static struct clients *scan_clients(struct clients *clients)
 {
 	struct dirent *dent;
 	struct client *c;
@@ -1022,7 +1109,7 @@ static void scan_clients(struct clients *clients)
 	DIR *d;
 
 	if (!clients)
-		return;
+		return clients;
 
 	for_each_client(clients, c, tmp) {
 		assert(c->status != PROBE);
@@ -1034,7 +1121,7 @@ static void scan_clients(struct clients *clients)
 
 	d = opendir(clients->sysfs_root);
 	if (!d)
-		return;
+		return clients;
 
 	while ((dent = readdir(d)) != NULL) {
 		char name[24], pid[24];
@@ -1077,7 +1164,7 @@ static void scan_clients(struct clients *clients)
 			break;
 	}
 
-	sort_clients(clients);
+	return display_clients(clients);
 }
 
 static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" };
@@ -2177,11 +2264,16 @@ static void select_client_sort(void)
 	};
 	static unsigned int client_sort;
 
+bump:
 	if (++client_sort >= ARRAY_SIZE(cmp))
 		client_sort = 0;
 
 	client_cmp = cmp[client_sort].cmp;
 	header_msg = cmp[client_sort].msg;
+
+	/* Sort by client id makes no sense with pid aggregation. */
+	if (aggregate_pids && client_cmp == client_id_cmp)
+		goto bump;
 }
 
 static void process_stdin(unsigned int timeout_us)
@@ -2227,6 +2319,13 @@ static void process_stdin(unsigned int timeout_us)
 		case 's':
 			select_client_sort();
 			break;
+		case 'H':
+			aggregate_pids ^= true;
+			if (aggregate_pids)
+				header_msg = "Aggregating clients.";
+			else
+				header_msg = "Showing individual clients.";
+			break;
 		};
 	}
 }
@@ -2378,6 +2477,7 @@ int main(int argc, char **argv)
 	codename = igt_device_get_pretty_name(&card, false);
 
 	while (!stop_top) {
+		struct clients *disp_clients;
 		bool consumed = false;
 		int j, lines = 0;
 		struct winsize ws;
@@ -2400,7 +2500,7 @@ int main(int argc, char **argv)
 		pmu_sample(engines);
 		t = (double)(engines->ts.cur - engines->ts.prev) / 1e9;
 
-		scan_clients(clients);
+		disp_clients = scan_clients(clients);
 
 		if (stop_top)
 			break;
@@ -2416,14 +2516,14 @@ int main(int argc, char **argv)
 
 			lines = print_engines(engines, t, lines, con_w, con_h);
 
-			if (clients) {
+			if (disp_clients) {
 				int class_w;
 
-				lines = print_clients_header(clients, lines,
+				lines = print_clients_header(disp_clients, lines,
 							     con_w, con_h,
 							     &class_w);
 
-				for_each_client(clients, c, j) {
+				for_each_client(disp_clients, c, j) {
 					assert(c->status != PROBE);
 					if (c->status != ALIVE)
 						break; /* Active clients are first in the array. */
@@ -2437,8 +2537,9 @@ int main(int argc, char **argv)
 							     &class_w);
 				}
 
-				lines = print_clients_footer(clients, t, lines,
-							     con_w, con_h);
+				lines = print_clients_footer(disp_clients, t,
+							     lines, con_w,
+							     con_h);
 			}
 
 			pops->close_struct();
@@ -2447,6 +2548,9 @@ int main(int argc, char **argv)
 		if (stop_top)
 			break;
 
+		if (disp_clients != clients)
+			free_clients(disp_clients);
+
 		if (output_mode == INTERACTIVE)
 			process_stdin(period_us);
 		else
-- 
2.27.0

_______________________________________________
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

* Re: [Intel-gfx] [igt-dev] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
  2021-02-10 10:35     ` Chris Wilson
@ 2021-02-10 10:55       ` Tvrtko Ursulin
  -1 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10 10:55 UTC (permalink / raw)
  To: Chris Wilson, igt-dev; +Cc: Intel-gfx


On 10/02/2021 10:35, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2021-02-10 09:37:55)
>> +static struct clients *aggregated_clients(struct clients *clients)
>> +{
>> +       struct client *ac, *c, *cp = NULL;
>> +       struct clients *aggregated;
>> +       int tmp, num = 0;
>> +
>> +       /* Sort by pid first to make it easy to aggregate while walking. */
>> +       sort_clients(clients, client_pid_cmp);
> 
> You could eliminate this tiny bit of duplication by always calling
> aggregated_clients() and returning here for !aggregate_pids.

Okay, I did something like that.

>> +       aggregated = calloc(1, sizeof(*clients));
>> +       assert(aggregated);
>> +
>> +       ac = calloc(clients->num_clients, sizeof(*c));
>> +       assert(ac);
>> +
>> +       aggregated->num_classes = clients->num_classes;
>> +       aggregated->class = clients->class;
>> +       aggregated->client = ac;
>> +
>> +       for_each_client(clients, c, tmp) {
>> +               unsigned int i;
>> +
>> +               if (c->status == FREE)
>> +                       break;
>> +
>> +               assert(c->status == ALIVE);
>> +
>> +               if ((cp && c->pid != cp->pid) || !cp) {
>> +                       ac = &aggregated->client[num];
>> +
>> +                       /* New pid. */
>> +                       ac->clients = aggregated;
>> +                       ac->status = ALIVE;
>> +                       ac->id = ++num;
>> +                       ac->pid = c->pid;
>> +                       strcpy(ac->name, c->name);
>> +                       strcpy(ac->print_name, c->print_name);
>> +                       ac->engines = c->engines;
>> +                       ac->val = calloc(clients->num_classes,
>> +                                        sizeof(ac->val[0]));
>> +                       assert(ac->val);
>> +                       ac->samples = 1;
>> +               }
>> +
>> +               cp = c;
>> +
>> +               if (c->samples < 2)
>> +                       continue;
>> +
>> +               ac->samples = 2; /* All what matters for display. */
>> +               ac->total_runtime += c->total_runtime;
>> +               ac->last_runtime += c->last_runtime;
>> +
>> +               for (i = 0; i < clients->num_classes; i++)
>> +                       ac->val[i] += c->val[i];
>> +       }
>> +
>> +       aggregated->num_clients = num;
>> +       aggregated->active_clients = num;
>> +
>> +       return sort_clients(aggregated, client_cmp);
>>   }
> 
> Ok, that works very well. Hmm. The sort order does seem a little jumpy
> though. May I suggest ac->id = -c->pid; instead of num;

Done it although I thought 1st pass being sort by pid already, num as id 
would follow a stable order. I guess your point was inversion to 
preserve order when cycling sort modes?

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] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
@ 2021-02-10 10:55       ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10 10:55 UTC (permalink / raw)
  To: Chris Wilson, igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin


On 10/02/2021 10:35, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2021-02-10 09:37:55)
>> +static struct clients *aggregated_clients(struct clients *clients)
>> +{
>> +       struct client *ac, *c, *cp = NULL;
>> +       struct clients *aggregated;
>> +       int tmp, num = 0;
>> +
>> +       /* Sort by pid first to make it easy to aggregate while walking. */
>> +       sort_clients(clients, client_pid_cmp);
> 
> You could eliminate this tiny bit of duplication by always calling
> aggregated_clients() and returning here for !aggregate_pids.

Okay, I did something like that.

>> +       aggregated = calloc(1, sizeof(*clients));
>> +       assert(aggregated);
>> +
>> +       ac = calloc(clients->num_clients, sizeof(*c));
>> +       assert(ac);
>> +
>> +       aggregated->num_classes = clients->num_classes;
>> +       aggregated->class = clients->class;
>> +       aggregated->client = ac;
>> +
>> +       for_each_client(clients, c, tmp) {
>> +               unsigned int i;
>> +
>> +               if (c->status == FREE)
>> +                       break;
>> +
>> +               assert(c->status == ALIVE);
>> +
>> +               if ((cp && c->pid != cp->pid) || !cp) {
>> +                       ac = &aggregated->client[num];
>> +
>> +                       /* New pid. */
>> +                       ac->clients = aggregated;
>> +                       ac->status = ALIVE;
>> +                       ac->id = ++num;
>> +                       ac->pid = c->pid;
>> +                       strcpy(ac->name, c->name);
>> +                       strcpy(ac->print_name, c->print_name);
>> +                       ac->engines = c->engines;
>> +                       ac->val = calloc(clients->num_classes,
>> +                                        sizeof(ac->val[0]));
>> +                       assert(ac->val);
>> +                       ac->samples = 1;
>> +               }
>> +
>> +               cp = c;
>> +
>> +               if (c->samples < 2)
>> +                       continue;
>> +
>> +               ac->samples = 2; /* All what matters for display. */
>> +               ac->total_runtime += c->total_runtime;
>> +               ac->last_runtime += c->last_runtime;
>> +
>> +               for (i = 0; i < clients->num_classes; i++)
>> +                       ac->val[i] += c->val[i];
>> +       }
>> +
>> +       aggregated->num_clients = num;
>> +       aggregated->active_clients = num;
>> +
>> +       return sort_clients(aggregated, client_cmp);
>>   }
> 
> Ok, that works very well. Hmm. The sort order does seem a little jumpy
> though. May I suggest ac->id = -c->pid; instead of num;

Done it although I thought 1st pass being sort by pid already, num as id 
would follow a stable order. I guess your point was inversion to 
preserve order when cycling sort modes?

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: [Intel-gfx] [igt-dev] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
  2021-02-10 10:55       ` Tvrtko Ursulin
  (?)
@ 2021-02-10 11:03       ` Chris Wilson
  -1 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2021-02-10 11:03 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2021-02-10 10:55:44)
> 
> On 10/02/2021 10:35, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2021-02-10 09:37:55)
> > Ok, that works very well. Hmm. The sort order does seem a little jumpy
> > though. May I suggest ac->id = -c->pid; instead of num;
> 
> Done it although I thought 1st pass being sort by pid already, num as id 
> would follow a stable order. I guess your point was inversion to 
> preserve order when cycling sort modes?

I thought that makes more sense for 'aggregate-by-pid', that it should
either be in ascending/descending pid order for idle.

It just felt very jumpy; it may have been tiny amount of %busy, but I
suspected it may have been slightly unstable sorting with changing
clients.
-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: [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
  2021-02-10 10:53   ` [Intel-gfx] " Tvrtko Ursulin
@ 2021-02-10 11:06       ` Chris Wilson
  0 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2021-02-10 11:06 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2021-02-10 10:53:43)
> +static struct clients *display_clients(struct clients *clients)
> +{
> +       struct client *ac, *c, *cp = NULL;
> +       struct clients *aggregated;
> +       int tmp, num = 0;
> +
> +       if (!aggregate_pids)
> +               return sort_clients(clients, client_cmp);

Still two calls to return sort_clients(foo, client_cmp) in this function
:) [a clients = aggregated; after processing would merge the two paths].

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] [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
@ 2021-02-10 11:06       ` Chris Wilson
  0 siblings, 0 replies; 20+ messages in thread
From: Chris Wilson @ 2021-02-10 11:06 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2021-02-10 10:53:43)
> +static struct clients *display_clients(struct clients *clients)
> +{
> +       struct client *ac, *c, *cp = NULL;
> +       struct clients *aggregated;
> +       int tmp, num = 0;
> +
> +       if (!aggregate_pids)
> +               return sort_clients(clients, client_cmp);

Still two calls to return sort_clients(foo, client_cmp) in this function
:) [a clients = aggregated; after processing would merge the two paths].

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

* [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
  2021-02-10  9:37 ` [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default Tvrtko Ursulin
@ 2021-02-10 11:12     ` Tvrtko Ursulin
  2021-02-10 10:35     ` Chris Wilson
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10 11:12 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Implement a default view where clients are aggregated by their PID.

Toggled by pressing 'H' similar to top(1).

v2:
 * Fix memory leak.

v3:
 * Do not allow sort by client id in aggregated mode.
 * Tweak sort criteria and sorting decisions. (Chris)

v4:
 * More tweaks to code flow. (Chris)

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 man/intel_gpu_top.rst |   1 +
 tools/intel_gpu_top.c | 133 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 121 insertions(+), 13 deletions(-)

diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index b145d85c0440..20658e291db0 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -58,6 +58,7 @@ Supported keys:
     'n'    Toggle display of numeric client busyness overlay.
     's'    Toggle between sort modes (runtime, total runtime, pid, client id).
     'i'    Toggle display of clients which used no GPU time.
+    'H'    Toggle between per PID aggregation and individual clients.
 
 DEVICE SELECTION
 ================
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 63ef77056341..e7f696110be8 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -979,17 +979,18 @@ static int client_pid_cmp(const void *_a, const void *_b)
 
 static int (*client_cmp)(const void *, const void *) = client_last_cmp;
 
-static void sort_clients(struct clients *clients)
+static struct clients *sort_clients(struct clients *clients,
+				    int (*cmp)(const void *, const void *))
 {
 	unsigned int active, free;
 	struct client *c;
 	int tmp;
 
 	if (!clients)
-		return;
+		return clients;
 
 	qsort(clients->client, clients->num_clients, sizeof(*clients->client),
-	      client_cmp);
+	      cmp);
 
 	/* Trim excessive array space. */
 	active = 0;
@@ -1011,9 +1012,98 @@ static void sort_clients(struct clients *clients)
 						  sizeof(*c));
 		}
 	}
+
+	return clients;
+}
+
+static bool aggregate_pids = true;
+
+static struct clients *display_clients(struct clients *clients)
+{
+	struct client *ac, *c, *cp = NULL;
+	struct clients *aggregated;
+	int tmp, num = 0;
+
+	if (!aggregate_pids)
+		goto out;
+
+	/* Sort by pid first to make it easy to aggregate while walking. */
+	sort_clients(clients, client_pid_cmp);
+
+	aggregated = calloc(1, sizeof(*clients));
+	assert(aggregated);
+
+	ac = calloc(clients->num_clients, sizeof(*c));
+	assert(ac);
+
+	aggregated->num_classes = clients->num_classes;
+	aggregated->class = clients->class;
+	aggregated->client = ac;
+
+	for_each_client(clients, c, tmp) {
+		unsigned int i;
+
+		if (c->status == FREE)
+			break;
+
+		assert(c->status == ALIVE);
+
+		if ((cp && c->pid != cp->pid) || !cp) {
+			ac = &aggregated->client[num++];
+
+			/* New pid. */
+			ac->clients = aggregated;
+			ac->status = ALIVE;
+			ac->id = -c->pid;
+			ac->pid = c->pid;
+			ac->busy_root = -1;
+			ac->sysfs_root = -1;
+			strcpy(ac->name, c->name);
+			strcpy(ac->print_name, c->print_name);
+			ac->engines = c->engines;
+			ac->val = calloc(clients->num_classes,
+					 sizeof(ac->val[0]));
+			assert(ac->val);
+			ac->samples = 1;
+		}
+
+		cp = c;
+
+		if (c->samples < 2)
+			continue;
+
+		ac->samples = 2; /* All what matters for display. */
+		ac->total_runtime += c->total_runtime;
+		ac->last_runtime += c->last_runtime;
+
+		for (i = 0; i < clients->num_classes; i++)
+			ac->val[i] += c->val[i];
+	}
+
+	aggregated->num_clients = num;
+	aggregated->active_clients = num;
+
+	clients = aggregated;
+
+out:
+	return sort_clients(clients, client_cmp);
 }
 
-static void scan_clients(struct clients *clients)
+static void free_clients(struct clients *clients)
+{
+	struct client *c;
+	unsigned int tmp;
+
+	for_each_client(clients, c, tmp) {
+		free(c->val);
+		free(c->last);
+	}
+
+	free(clients->client);
+	free(clients);
+}
+
+static struct clients *scan_clients(struct clients *clients)
 {
 	struct dirent *dent;
 	struct client *c;
@@ -1022,7 +1112,7 @@ static void scan_clients(struct clients *clients)
 	DIR *d;
 
 	if (!clients)
-		return;
+		return clients;
 
 	for_each_client(clients, c, tmp) {
 		assert(c->status != PROBE);
@@ -1034,7 +1124,7 @@ static void scan_clients(struct clients *clients)
 
 	d = opendir(clients->sysfs_root);
 	if (!d)
-		return;
+		return clients;
 
 	while ((dent = readdir(d)) != NULL) {
 		char name[24], pid[24];
@@ -1077,7 +1167,7 @@ static void scan_clients(struct clients *clients)
 			break;
 	}
 
-	sort_clients(clients);
+	return display_clients(clients);
 }
 
 static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" };
@@ -2177,11 +2267,16 @@ static void select_client_sort(void)
 	};
 	static unsigned int client_sort;
 
+bump:
 	if (++client_sort >= ARRAY_SIZE(cmp))
 		client_sort = 0;
 
 	client_cmp = cmp[client_sort].cmp;
 	header_msg = cmp[client_sort].msg;
+
+	/* Sort by client id makes no sense with pid aggregation. */
+	if (aggregate_pids && client_cmp == client_id_cmp)
+		goto bump;
 }
 
 static void process_stdin(unsigned int timeout_us)
@@ -2227,6 +2322,13 @@ static void process_stdin(unsigned int timeout_us)
 		case 's':
 			select_client_sort();
 			break;
+		case 'H':
+			aggregate_pids ^= true;
+			if (aggregate_pids)
+				header_msg = "Aggregating clients.";
+			else
+				header_msg = "Showing individual clients.";
+			break;
 		};
 	}
 }
@@ -2378,6 +2480,7 @@ int main(int argc, char **argv)
 	codename = igt_device_get_pretty_name(&card, false);
 
 	while (!stop_top) {
+		struct clients *disp_clients;
 		bool consumed = false;
 		int j, lines = 0;
 		struct winsize ws;
@@ -2400,7 +2503,7 @@ int main(int argc, char **argv)
 		pmu_sample(engines);
 		t = (double)(engines->ts.cur - engines->ts.prev) / 1e9;
 
-		scan_clients(clients);
+		disp_clients = scan_clients(clients);
 
 		if (stop_top)
 			break;
@@ -2416,14 +2519,14 @@ int main(int argc, char **argv)
 
 			lines = print_engines(engines, t, lines, con_w, con_h);
 
-			if (clients) {
+			if (disp_clients) {
 				int class_w;
 
-				lines = print_clients_header(clients, lines,
+				lines = print_clients_header(disp_clients, lines,
 							     con_w, con_h,
 							     &class_w);
 
-				for_each_client(clients, c, j) {
+				for_each_client(disp_clients, c, j) {
 					assert(c->status != PROBE);
 					if (c->status != ALIVE)
 						break; /* Active clients are first in the array. */
@@ -2437,8 +2540,9 @@ int main(int argc, char **argv)
 							     &class_w);
 				}
 
-				lines = print_clients_footer(clients, t, lines,
-							     con_w, con_h);
+				lines = print_clients_footer(disp_clients, t,
+							     lines, con_w,
+							     con_h);
 			}
 
 			pops->close_struct();
@@ -2447,6 +2551,9 @@ int main(int argc, char **argv)
 		if (stop_top)
 			break;
 
+		if (disp_clients != clients)
+			free_clients(disp_clients);
+
 		if (output_mode == INTERACTIVE)
 			process_stdin(period_us);
 		else
-- 
2.27.0

_______________________________________________
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] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default
@ 2021-02-10 11:12     ` Tvrtko Ursulin
  0 siblings, 0 replies; 20+ messages in thread
From: Tvrtko Ursulin @ 2021-02-10 11:12 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Implement a default view where clients are aggregated by their PID.

Toggled by pressing 'H' similar to top(1).

v2:
 * Fix memory leak.

v3:
 * Do not allow sort by client id in aggregated mode.
 * Tweak sort criteria and sorting decisions. (Chris)

v4:
 * More tweaks to code flow. (Chris)

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 man/intel_gpu_top.rst |   1 +
 tools/intel_gpu_top.c | 133 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 121 insertions(+), 13 deletions(-)

diff --git a/man/intel_gpu_top.rst b/man/intel_gpu_top.rst
index b145d85c0440..20658e291db0 100644
--- a/man/intel_gpu_top.rst
+++ b/man/intel_gpu_top.rst
@@ -58,6 +58,7 @@ Supported keys:
     'n'    Toggle display of numeric client busyness overlay.
     's'    Toggle between sort modes (runtime, total runtime, pid, client id).
     'i'    Toggle display of clients which used no GPU time.
+    'H'    Toggle between per PID aggregation and individual clients.
 
 DEVICE SELECTION
 ================
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index 63ef77056341..e7f696110be8 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -979,17 +979,18 @@ static int client_pid_cmp(const void *_a, const void *_b)
 
 static int (*client_cmp)(const void *, const void *) = client_last_cmp;
 
-static void sort_clients(struct clients *clients)
+static struct clients *sort_clients(struct clients *clients,
+				    int (*cmp)(const void *, const void *))
 {
 	unsigned int active, free;
 	struct client *c;
 	int tmp;
 
 	if (!clients)
-		return;
+		return clients;
 
 	qsort(clients->client, clients->num_clients, sizeof(*clients->client),
-	      client_cmp);
+	      cmp);
 
 	/* Trim excessive array space. */
 	active = 0;
@@ -1011,9 +1012,98 @@ static void sort_clients(struct clients *clients)
 						  sizeof(*c));
 		}
 	}
+
+	return clients;
+}
+
+static bool aggregate_pids = true;
+
+static struct clients *display_clients(struct clients *clients)
+{
+	struct client *ac, *c, *cp = NULL;
+	struct clients *aggregated;
+	int tmp, num = 0;
+
+	if (!aggregate_pids)
+		goto out;
+
+	/* Sort by pid first to make it easy to aggregate while walking. */
+	sort_clients(clients, client_pid_cmp);
+
+	aggregated = calloc(1, sizeof(*clients));
+	assert(aggregated);
+
+	ac = calloc(clients->num_clients, sizeof(*c));
+	assert(ac);
+
+	aggregated->num_classes = clients->num_classes;
+	aggregated->class = clients->class;
+	aggregated->client = ac;
+
+	for_each_client(clients, c, tmp) {
+		unsigned int i;
+
+		if (c->status == FREE)
+			break;
+
+		assert(c->status == ALIVE);
+
+		if ((cp && c->pid != cp->pid) || !cp) {
+			ac = &aggregated->client[num++];
+
+			/* New pid. */
+			ac->clients = aggregated;
+			ac->status = ALIVE;
+			ac->id = -c->pid;
+			ac->pid = c->pid;
+			ac->busy_root = -1;
+			ac->sysfs_root = -1;
+			strcpy(ac->name, c->name);
+			strcpy(ac->print_name, c->print_name);
+			ac->engines = c->engines;
+			ac->val = calloc(clients->num_classes,
+					 sizeof(ac->val[0]));
+			assert(ac->val);
+			ac->samples = 1;
+		}
+
+		cp = c;
+
+		if (c->samples < 2)
+			continue;
+
+		ac->samples = 2; /* All what matters for display. */
+		ac->total_runtime += c->total_runtime;
+		ac->last_runtime += c->last_runtime;
+
+		for (i = 0; i < clients->num_classes; i++)
+			ac->val[i] += c->val[i];
+	}
+
+	aggregated->num_clients = num;
+	aggregated->active_clients = num;
+
+	clients = aggregated;
+
+out:
+	return sort_clients(clients, client_cmp);
 }
 
-static void scan_clients(struct clients *clients)
+static void free_clients(struct clients *clients)
+{
+	struct client *c;
+	unsigned int tmp;
+
+	for_each_client(clients, c, tmp) {
+		free(c->val);
+		free(c->last);
+	}
+
+	free(clients->client);
+	free(clients);
+}
+
+static struct clients *scan_clients(struct clients *clients)
 {
 	struct dirent *dent;
 	struct client *c;
@@ -1022,7 +1112,7 @@ static void scan_clients(struct clients *clients)
 	DIR *d;
 
 	if (!clients)
-		return;
+		return clients;
 
 	for_each_client(clients, c, tmp) {
 		assert(c->status != PROBE);
@@ -1034,7 +1124,7 @@ static void scan_clients(struct clients *clients)
 
 	d = opendir(clients->sysfs_root);
 	if (!d)
-		return;
+		return clients;
 
 	while ((dent = readdir(d)) != NULL) {
 		char name[24], pid[24];
@@ -1077,7 +1167,7 @@ static void scan_clients(struct clients *clients)
 			break;
 	}
 
-	sort_clients(clients);
+	return display_clients(clients);
 }
 
 static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" };
@@ -2177,11 +2267,16 @@ static void select_client_sort(void)
 	};
 	static unsigned int client_sort;
 
+bump:
 	if (++client_sort >= ARRAY_SIZE(cmp))
 		client_sort = 0;
 
 	client_cmp = cmp[client_sort].cmp;
 	header_msg = cmp[client_sort].msg;
+
+	/* Sort by client id makes no sense with pid aggregation. */
+	if (aggregate_pids && client_cmp == client_id_cmp)
+		goto bump;
 }
 
 static void process_stdin(unsigned int timeout_us)
@@ -2227,6 +2322,13 @@ static void process_stdin(unsigned int timeout_us)
 		case 's':
 			select_client_sort();
 			break;
+		case 'H':
+			aggregate_pids ^= true;
+			if (aggregate_pids)
+				header_msg = "Aggregating clients.";
+			else
+				header_msg = "Showing individual clients.";
+			break;
 		};
 	}
 }
@@ -2378,6 +2480,7 @@ int main(int argc, char **argv)
 	codename = igt_device_get_pretty_name(&card, false);
 
 	while (!stop_top) {
+		struct clients *disp_clients;
 		bool consumed = false;
 		int j, lines = 0;
 		struct winsize ws;
@@ -2400,7 +2503,7 @@ int main(int argc, char **argv)
 		pmu_sample(engines);
 		t = (double)(engines->ts.cur - engines->ts.prev) / 1e9;
 
-		scan_clients(clients);
+		disp_clients = scan_clients(clients);
 
 		if (stop_top)
 			break;
@@ -2416,14 +2519,14 @@ int main(int argc, char **argv)
 
 			lines = print_engines(engines, t, lines, con_w, con_h);
 
-			if (clients) {
+			if (disp_clients) {
 				int class_w;
 
-				lines = print_clients_header(clients, lines,
+				lines = print_clients_header(disp_clients, lines,
 							     con_w, con_h,
 							     &class_w);
 
-				for_each_client(clients, c, j) {
+				for_each_client(disp_clients, c, j) {
 					assert(c->status != PROBE);
 					if (c->status != ALIVE)
 						break; /* Active clients are first in the array. */
@@ -2437,8 +2540,9 @@ int main(int argc, char **argv)
 							     &class_w);
 				}
 
-				lines = print_clients_footer(clients, t, lines,
-							     con_w, con_h);
+				lines = print_clients_footer(disp_clients, t,
+							     lines, con_w,
+							     con_h);
 			}
 
 			pops->close_struct();
@@ -2447,6 +2551,9 @@ int main(int argc, char **argv)
 		if (stop_top)
 			break;
 
+		if (disp_clients != clients)
+			free_clients(disp_clients);
+
 		if (output_mode == INTERACTIVE)
 			process_stdin(period_us);
 		else
-- 
2.27.0

_______________________________________________
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-dev] ✗ Fi.CI.BUILD: failure for series starting with [i-g-t,1/3] intel_gpu_top: Wrap interactive header (rev4)
  2021-02-10  9:37 ` [igt-dev] " Tvrtko Ursulin
                   ` (3 preceding siblings ...)
  (?)
@ 2021-02-10 11:35 ` Patchwork
  -1 siblings, 0 replies; 20+ messages in thread
From: Patchwork @ 2021-02-10 11:35 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: series starting with [i-g-t,1/3] intel_gpu_top: Wrap interactive header (rev4)
URL   : https://patchwork.freedesktop.org/series/86938/
State : failure

== Summary ==

Applying: intel_gpu_top: Wrap interactive header
Applying: intel_gpu_top: Aggregate clients by PID by default
Applying: intel_gpu_top: Interactive help screen
Patch failed at 0003 intel_gpu_top: Interactive help screen
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".


_______________________________________________
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

end of thread, other threads:[~2021-02-10 11:35 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-10  9:37 [Intel-gfx] [PATCH i-g-t 1/3] intel_gpu_top: Wrap interactive header Tvrtko Ursulin
2021-02-10  9:37 ` [igt-dev] " Tvrtko Ursulin
2021-02-10  9:37 ` [Intel-gfx] [PATCH i-g-t 2/3] intel_gpu_top: Aggregate clients by PID by default Tvrtko Ursulin
2021-02-10  9:44   ` Tvrtko Ursulin
2021-02-10 10:35   ` [Intel-gfx] [igt-dev] " Chris Wilson
2021-02-10 10:35     ` Chris Wilson
2021-02-10 10:55     ` [Intel-gfx] " Tvrtko Ursulin
2021-02-10 10:55       ` Tvrtko Ursulin
2021-02-10 11:03       ` [Intel-gfx] " Chris Wilson
2021-02-10 10:53   ` [Intel-gfx] " Tvrtko Ursulin
2021-02-10 11:06     ` Chris Wilson
2021-02-10 11:06       ` [igt-dev] " Chris Wilson
2021-02-10 11:12   ` Tvrtko Ursulin
2021-02-10 11:12     ` [igt-dev] " Tvrtko Ursulin
2021-02-10  9:37 ` [Intel-gfx] [PATCH i-g-t 3/3] intel_gpu_top: Interactive help screen Tvrtko Ursulin
2021-02-10  9:37   ` [igt-dev] " Tvrtko Ursulin
2021-02-10 10:36   ` [Intel-gfx] " Chris Wilson
2021-02-10 10:36     ` Chris Wilson
2021-02-10 10:37 ` [Intel-gfx] [PATCH i-g-t 1/3] intel_gpu_top: Wrap interactive header Chris Wilson
2021-02-10 11:35 ` [igt-dev] ✗ Fi.CI.BUILD: failure for series starting with [i-g-t,1/3] intel_gpu_top: Wrap interactive header (rev4) Patchwork

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.