• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2007-2019 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <dirent.h>
27 #include <stdint.h>
28 #include <assert.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <inttypes.h>
36 #include <sys/ioctl.h>
37 #include <errno.h>
38 #include <math.h>
39 #include <locale.h>
40 #include <limits.h>
41 #include <signal.h>
42 
43 #include "igt_perf.h"
44 
45 struct pmu_pair {
46 	uint64_t cur;
47 	uint64_t prev;
48 };
49 
50 struct pmu_counter {
51 	bool present;
52 	uint64_t config;
53 	unsigned int idx;
54 	struct pmu_pair val;
55 };
56 
57 struct engine {
58 	const char *name;
59 	char *display_name;
60 	char *short_name;
61 
62 	unsigned int class;
63 	unsigned int instance;
64 
65 	unsigned int num_counters;
66 
67 	struct pmu_counter busy;
68 	struct pmu_counter wait;
69 	struct pmu_counter sema;
70 };
71 
72 struct engines {
73 	unsigned int num_engines;
74 	unsigned int num_counters;
75 	DIR *root;
76 	int fd;
77 	struct pmu_pair ts;
78 
79 	int rapl_fd;
80 	double rapl_scale;
81 	const char *rapl_unit;
82 
83 	int imc_fd;
84 	double imc_reads_scale;
85 	const char *imc_reads_unit;
86 	double imc_writes_scale;
87 	const char *imc_writes_unit;
88 
89 	struct pmu_counter freq_req;
90 	struct pmu_counter freq_act;
91 	struct pmu_counter irq;
92 	struct pmu_counter rc6;
93 	struct pmu_counter rapl;
94 	struct pmu_counter imc_reads;
95 	struct pmu_counter imc_writes;
96 
97 	struct engine engine;
98 };
99 
100 static uint64_t
get_pmu_config(int dirfd,const char * name,const char * counter)101 get_pmu_config(int dirfd, const char *name, const char *counter)
102 {
103 	char buf[128], *p;
104 	int fd, ret;
105 
106 	ret = snprintf(buf, sizeof(buf), "%s-%s", name, counter);
107 	if (ret < 0 || ret == sizeof(buf))
108 		return -1;
109 
110 	fd = openat(dirfd, buf, O_RDONLY);
111 	if (fd < 0)
112 		return -1;
113 
114 	ret = read(fd, buf, sizeof(buf));
115 	close(fd);
116 	if (ret <= 0)
117 		return -1;
118 
119 	p = index(buf, '0');
120 	if (!p)
121 		return -1;
122 
123 	return strtoul(p, NULL, 0);
124 }
125 
126 #define engine_ptr(engines, n) (&engines->engine + (n))
127 
class_display_name(unsigned int class)128 static const char *class_display_name(unsigned int class)
129 {
130 	switch (class) {
131 	case I915_ENGINE_CLASS_RENDER:
132 		return "Render/3D";
133 	case I915_ENGINE_CLASS_COPY:
134 		return "Blitter";
135 	case I915_ENGINE_CLASS_VIDEO:
136 		return "Video";
137 	case I915_ENGINE_CLASS_VIDEO_ENHANCE:
138 		return "VideoEnhance";
139 	default:
140 		return "[unknown]";
141 	}
142 }
143 
class_short_name(unsigned int class)144 static const char *class_short_name(unsigned int class)
145 {
146 	switch (class) {
147 	case I915_ENGINE_CLASS_RENDER:
148 		return "RCS";
149 	case I915_ENGINE_CLASS_COPY:
150 		return "BCS";
151 	case I915_ENGINE_CLASS_VIDEO:
152 		return "VCS";
153 	case I915_ENGINE_CLASS_VIDEO_ENHANCE:
154 		return "VECS";
155 	default:
156 		return "UNKN";
157 	}
158 }
159 
engine_cmp(const void * __a,const void * __b)160 static int engine_cmp(const void *__a, const void *__b)
161 {
162 	const struct engine *a = (struct engine *)__a;
163 	const struct engine *b = (struct engine *)__b;
164 
165 	if (a->class != b->class)
166 		return a->class - b->class;
167 	else
168 		return a->instance - b->instance;
169 }
170 
discover_engines(void)171 static struct engines *discover_engines(void)
172 {
173 	const char *sysfs_root = "/sys/devices/i915/events";
174 	struct engines *engines;
175 	struct dirent *dent;
176 	int ret = 0;
177 	DIR *d;
178 
179 	engines = malloc(sizeof(struct engines));
180 	if (!engines)
181 		return NULL;
182 
183 	memset(engines, 0, sizeof(*engines));
184 
185 	engines->num_engines = 0;
186 
187 	d = opendir(sysfs_root);
188 	if (!d)
189 		return NULL;
190 
191 	while ((dent = readdir(d)) != NULL) {
192 		const char *endswith = "-busy";
193 		const unsigned int endlen = strlen(endswith);
194 		struct engine *engine =
195 				engine_ptr(engines, engines->num_engines);
196 		char buf[256];
197 
198 		if (dent->d_type != DT_REG)
199 			continue;
200 
201 		if (strlen(dent->d_name) >= sizeof(buf)) {
202 			ret = ENAMETOOLONG;
203 			break;
204 		}
205 
206 		strcpy(buf, dent->d_name);
207 
208 		/* xxxN-busy */
209 		if (strlen(buf) < (endlen + 4))
210 			continue;
211 		if (strcmp(&buf[strlen(buf) - endlen], endswith))
212 			continue;
213 
214 		memset(engine, 0, sizeof(*engine));
215 
216 		buf[strlen(buf) - endlen] = 0;
217 		engine->name = strdup(buf);
218 		if (!engine->name) {
219 			ret = errno;
220 			break;
221 		}
222 
223 		engine->busy.config = get_pmu_config(dirfd(d), engine->name,
224 						     "busy");
225 		if (engine->busy.config == -1) {
226 			ret = ENOENT;
227 			break;
228 		}
229 
230 		engine->class = (engine->busy.config &
231 				 (__I915_PMU_OTHER(0) - 1)) >>
232 				I915_PMU_CLASS_SHIFT;
233 
234 		engine->instance = (engine->busy.config >>
235 				    I915_PMU_SAMPLE_BITS) &
236 				    ((1 << I915_PMU_SAMPLE_INSTANCE_BITS) - 1);
237 
238 		ret = asprintf(&engine->display_name, "%s/%u",
239 			       class_display_name(engine->class),
240 			       engine->instance);
241 		if (ret <= 0) {
242 			ret = errno;
243 			break;
244 		}
245 
246 		ret = asprintf(&engine->short_name, "%s/%u",
247 			       class_short_name(engine->class),
248 			       engine->instance);
249 		if (ret <= 0) {
250 			ret = errno;
251 			break;
252 		}
253 
254 		engines->num_engines++;
255 		engines = realloc(engines, sizeof(struct engines) +
256 				  engines->num_engines * sizeof(struct engine));
257 		if (!engines) {
258 			ret = errno;
259 			break;
260 		}
261 
262 		ret = 0;
263 	}
264 
265 	if (ret) {
266 		free(engines);
267 		errno = ret;
268 
269 		return NULL;
270 	}
271 
272 	qsort(engine_ptr(engines, 0), engines->num_engines,
273 	      sizeof(struct engine), engine_cmp);
274 
275 	engines->root = d;
276 
277 	return engines;
278 }
279 
280 static int
filename_to_buf(const char * filename,char * buf,unsigned int bufsize)281 filename_to_buf(const char *filename, char *buf, unsigned int bufsize)
282 {
283 	int fd, err;
284 	ssize_t ret;
285 
286 	fd = open(filename, O_RDONLY);
287 	if (fd < 0)
288 		return -1;
289 
290 	ret = read(fd, buf, bufsize - 1);
291 	err = errno;
292 	close(fd);
293 	if (ret < 1) {
294 		errno = ret < 0 ? err : ENOMSG;
295 
296 		return -1;
297 	}
298 
299 	if (ret > 1 && buf[ret - 1] == '\n')
300 		buf[ret - 1] = '\0';
301 	else
302 		buf[ret] = '\0';
303 
304 	return 0;
305 }
306 
filename_to_u64(const char * filename,int base)307 static uint64_t filename_to_u64(const char *filename, int base)
308 {
309 	char buf[64], *b;
310 
311 	if (filename_to_buf(filename, buf, sizeof(buf)))
312 		return 0;
313 
314 	/*
315 	 * Handle both single integer and key=value formats by skipping
316 	 * leading non-digits.
317 	 */
318 	b = buf;
319 	while (*b && !isdigit(*b))
320 		b++;
321 
322 	return strtoull(b, NULL, base);
323 }
324 
filename_to_double(const char * filename)325 static double filename_to_double(const char *filename)
326 {
327 	char *oldlocale;
328 	char buf[80];
329 	double v;
330 
331 	if (filename_to_buf(filename, buf, sizeof(buf)))
332 		return 0;
333 
334 	oldlocale = setlocale(LC_ALL, "C");
335 	v = strtod(buf, NULL);
336 	setlocale(LC_ALL, oldlocale);
337 
338 	return v;
339 }
340 
341 #define RAPL_ROOT "/sys/devices/power/"
342 #define RAPL_EVENT "/sys/devices/power/events/"
343 
rapl_type_id(void)344 static uint64_t rapl_type_id(void)
345 {
346 	return filename_to_u64(RAPL_ROOT "type", 10);
347 }
348 
rapl_gpu_power(void)349 static uint64_t rapl_gpu_power(void)
350 {
351 	return filename_to_u64(RAPL_EVENT "energy-gpu", 0);
352 }
353 
rapl_gpu_power_scale(void)354 static double rapl_gpu_power_scale(void)
355 {
356 	return filename_to_double(RAPL_EVENT "energy-gpu.scale");
357 }
358 
rapl_gpu_power_unit(void)359 static const char *rapl_gpu_power_unit(void)
360 {
361 	char buf[32];
362 
363 	if (filename_to_buf(RAPL_EVENT "energy-gpu.unit",
364 			    buf, sizeof(buf)) == 0)
365 		if (!strcmp(buf, "Joules"))
366 			return strdup("Watts");
367 		else
368 			return strdup(buf);
369 	else
370 		return NULL;
371 }
372 
373 #define IMC_ROOT "/sys/devices/uncore_imc/"
374 #define IMC_EVENT "/sys/devices/uncore_imc/events/"
375 
imc_type_id(void)376 static uint64_t imc_type_id(void)
377 {
378 	return filename_to_u64(IMC_ROOT "type", 10);
379 }
380 
imc_data_reads(void)381 static uint64_t imc_data_reads(void)
382 {
383 	return filename_to_u64(IMC_EVENT "data_reads", 0);
384 }
385 
imc_data_reads_scale(void)386 static double imc_data_reads_scale(void)
387 {
388 	return filename_to_double(IMC_EVENT "data_reads.scale");
389 }
390 
imc_data_reads_unit(void)391 static const char *imc_data_reads_unit(void)
392 {
393 	char buf[32];
394 
395 	if (filename_to_buf(IMC_EVENT "data_reads.unit", buf, sizeof(buf)) == 0)
396 		return strdup(buf);
397 	else
398 		return NULL;
399 }
400 
imc_data_writes(void)401 static uint64_t imc_data_writes(void)
402 {
403 	return filename_to_u64(IMC_EVENT "data_writes", 0);
404 }
405 
imc_data_writes_scale(void)406 static double imc_data_writes_scale(void)
407 {
408 	return filename_to_double(IMC_EVENT "data_writes.scale");
409 }
410 
imc_data_writes_unit(void)411 static const char *imc_data_writes_unit(void)
412 {
413 	char buf[32];
414 
415 	if (filename_to_buf(IMC_EVENT "data_writes.unit",
416 			    buf, sizeof(buf)) == 0)
417 		return strdup(buf);
418 	else
419 		return NULL;
420 }
421 
422 #define _open_pmu(cnt, pmu, fd) \
423 ({ \
424 	int fd__; \
425 \
426 	fd__ = perf_i915_open_group((pmu)->config, (fd)); \
427 	if (fd__ >= 0) { \
428 		if ((fd) == -1) \
429 			(fd) = fd__; \
430 		(pmu)->present = true; \
431 		(pmu)->idx = (cnt)++; \
432 	} \
433 \
434 	fd__; \
435 })
436 
437 #define _open_imc(cnt, pmu, fd) \
438 ({ \
439 	int fd__; \
440 \
441 	fd__ = igt_perf_open_group(imc_type_id(), (pmu)->config, (fd)); \
442 	if (fd__ >= 0) { \
443 		if ((fd) == -1) \
444 			(fd) = fd__; \
445 		(pmu)->present = true; \
446 		(pmu)->idx = (cnt)++; \
447 	} \
448 \
449 	fd__; \
450 })
451 
pmu_init(struct engines * engines)452 static int pmu_init(struct engines *engines)
453 {
454 	unsigned int i;
455 	int fd;
456 
457 	engines->fd = -1;
458 	engines->num_counters = 0;
459 
460 	engines->irq.config = I915_PMU_INTERRUPTS;
461 	fd = _open_pmu(engines->num_counters, &engines->irq, engines->fd);
462 	if (fd < 0)
463 		return -1;
464 
465 	engines->freq_req.config = I915_PMU_REQUESTED_FREQUENCY;
466 	_open_pmu(engines->num_counters, &engines->freq_req, engines->fd);
467 
468 	engines->freq_act.config = I915_PMU_ACTUAL_FREQUENCY;
469 	_open_pmu(engines->num_counters, &engines->freq_act, engines->fd);
470 
471 	engines->rc6.config = I915_PMU_RC6_RESIDENCY;
472 	_open_pmu(engines->num_counters, &engines->rc6, engines->fd);
473 
474 	for (i = 0; i < engines->num_engines; i++) {
475 		struct engine *engine = engine_ptr(engines, i);
476 		struct {
477 			struct pmu_counter *pmu;
478 			const char *counter;
479 		} *cnt, counters[] = {
480 			{ .pmu = &engine->busy, .counter = "busy" },
481 			{ .pmu = &engine->wait, .counter = "wait" },
482 			{ .pmu = &engine->sema, .counter = "sema" },
483 			{ .pmu = NULL, .counter = NULL },
484 		};
485 
486 		for (cnt = counters; cnt->pmu; cnt++) {
487 			if (!cnt->pmu->config)
488 				cnt->pmu->config =
489 					get_pmu_config(dirfd(engines->root),
490 						       engine->name,
491 						       cnt->counter);
492 			fd = _open_pmu(engines->num_counters, cnt->pmu,
493 				       engines->fd);
494 			if (fd >= 0)
495 				engine->num_counters++;
496 		}
497 	}
498 
499 	engines->rapl_fd = -1;
500 	if (rapl_type_id()) {
501 		engines->rapl_scale = rapl_gpu_power_scale();
502 		engines->rapl_unit = rapl_gpu_power_unit();
503 		if (!engines->rapl_unit)
504 			return -1;
505 
506 		engines->rapl.config = rapl_gpu_power();
507 		if (!engines->rapl.config)
508 			return -1;
509 
510 		engines->rapl_fd = igt_perf_open(rapl_type_id(),
511 						 engines->rapl.config);
512 		if (engines->rapl_fd < 0)
513 			return -1;
514 
515 		engines->rapl.present = true;
516 	}
517 
518 	engines->imc_fd = -1;
519 	if (imc_type_id()) {
520 		unsigned int num = 0;
521 
522 		engines->imc_reads_scale = imc_data_reads_scale();
523 		engines->imc_writes_scale = imc_data_writes_scale();
524 
525 		engines->imc_reads_unit = imc_data_reads_unit();
526 		if (!engines->imc_reads_unit)
527 			return -1;
528 
529 		engines->imc_writes_unit = imc_data_writes_unit();
530 		if (!engines->imc_writes_unit)
531 			return -1;
532 
533 		engines->imc_reads.config = imc_data_reads();
534 		if (!engines->imc_reads.config)
535 			return -1;
536 
537 		engines->imc_writes.config = imc_data_writes();
538 		if (!engines->imc_writes.config)
539 			return -1;
540 
541 		fd = _open_imc(num, &engines->imc_reads, engines->imc_fd);
542 		if (fd < 0)
543 			return -1;
544 		fd = _open_imc(num, &engines->imc_writes, engines->imc_fd);
545 		if (fd < 0)
546 			return -1;
547 
548 		engines->imc_reads.present = true;
549 		engines->imc_writes.present = true;
550 	}
551 
552 	return 0;
553 }
554 
pmu_read_multi(int fd,unsigned int num,uint64_t * val)555 static uint64_t pmu_read_multi(int fd, unsigned int num, uint64_t *val)
556 {
557 	uint64_t buf[2 + num];
558 	unsigned int i;
559 	ssize_t len;
560 
561 	memset(buf, 0, sizeof(buf));
562 
563 	len = read(fd, buf, sizeof(buf));
564 	assert(len == sizeof(buf));
565 
566 	for (i = 0; i < num; i++)
567 		val[i] = buf[2 + i];
568 
569 	return buf[1];
570 }
571 
pmu_calc(struct pmu_pair * p,double d,double t,double s)572 static double pmu_calc(struct pmu_pair *p, double d, double t, double s)
573 {
574 	double v;
575 
576 	v = p->cur - p->prev;
577 	v /= d;
578 	v /= t;
579 	v *= s;
580 
581 	if (s == 100.0 && v > 100.0)
582 		v = 100.0;
583 
584 	return v;
585 }
586 
fill_str(char * buf,unsigned int bufsz,char c,unsigned int num)587 static void fill_str(char *buf, unsigned int bufsz, char c, unsigned int num)
588 {
589 	unsigned int i;
590 
591 	for (i = 0; i < num && i < (bufsz - 1); i++)
592 		*buf++ = c;
593 
594 	*buf = 0;
595 }
596 
__pmu_read_single(int fd,uint64_t * ts)597 static uint64_t __pmu_read_single(int fd, uint64_t *ts)
598 {
599 	uint64_t data[2] = { };
600 	ssize_t len;
601 
602 	len = read(fd, data, sizeof(data));
603 	assert(len == sizeof(data));
604 
605 	if (ts)
606 		*ts = data[1];
607 
608 	return data[0];
609 }
610 
pmu_read_single(int fd)611 static uint64_t pmu_read_single(int fd)
612 {
613 	return __pmu_read_single(fd, NULL);
614 }
615 
__update_sample(struct pmu_counter * counter,uint64_t val)616 static void __update_sample(struct pmu_counter *counter, uint64_t val)
617 {
618 	counter->val.prev = counter->val.cur;
619 	counter->val.cur = val;
620 }
621 
update_sample(struct pmu_counter * counter,uint64_t * val)622 static void update_sample(struct pmu_counter *counter, uint64_t *val)
623 {
624 	if (counter->present)
625 		__update_sample(counter, val[counter->idx]);
626 }
627 
pmu_sample(struct engines * engines)628 static void pmu_sample(struct engines *engines)
629 {
630 	const int num_val = engines->num_counters;
631 	uint64_t val[2 + num_val];
632 	unsigned int i;
633 
634 	engines->ts.prev = engines->ts.cur;
635 
636 	if (engines->rapl_fd >= 0)
637 		__update_sample(&engines->rapl,
638 				pmu_read_single(engines->rapl_fd));
639 
640 	if (engines->imc_fd >= 0) {
641 		pmu_read_multi(engines->imc_fd, 2, val);
642 		update_sample(&engines->imc_reads, val);
643 		update_sample(&engines->imc_writes, val);
644 	}
645 
646 	engines->ts.cur = pmu_read_multi(engines->fd, num_val, val);
647 
648 	update_sample(&engines->freq_req, val);
649 	update_sample(&engines->freq_act, val);
650 	update_sample(&engines->irq, val);
651 	update_sample(&engines->rc6, val);
652 
653 	for (i = 0; i < engines->num_engines; i++) {
654 		struct engine *engine = engine_ptr(engines, i);
655 
656 		update_sample(&engine->busy, val);
657 		update_sample(&engine->sema, val);
658 		update_sample(&engine->wait, val);
659 	}
660 }
661 
662 static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" };
663 
664 static void
print_percentage_bar(double percent,int max_len)665 print_percentage_bar(double percent, int max_len)
666 {
667 	int bar_len = percent * (8 * (max_len - 2)) / 100.0;
668 	int i;
669 
670 	putchar('|');
671 
672 	for (i = bar_len; i >= 8; i -= 8)
673 		printf("%s", bars[8]);
674 	if (i)
675 		printf("%s", bars[i]);
676 
677 	for (i = 0; i < (max_len - 2 - (bar_len + 7) / 8); i++)
678 		putchar(' ');
679 
680 	putchar('|');
681 }
682 
683 #define DEFAULT_PERIOD_MS (1000)
684 
685 static void
usage(const char * appname)686 usage(const char *appname)
687 {
688 	printf("intel_gpu_top - Display a top-like summary of Intel GPU usage\n"
689 		"\n"
690 		"Usage: %s [parameters]\n"
691 		"\n"
692 		"\tThe following parameters are optional:\n\n"
693 		"\t[-h]            Show this help text.\n"
694 		"\t[-J]            Output JSON formatted data.\n"
695 		"\t[-l]            List plain text data.\n"
696 		"\t[-o <file|->]   Output to specified file or '-' for standard out.\n"
697 		"\t[-s <ms>]       Refresh period in milliseconds (default %ums).\n"
698 		"\n",
699 		appname, DEFAULT_PERIOD_MS);
700 }
701 
702 static enum {
703 	INTERACTIVE,
704 	STDOUT,
705 	JSON
706 } output_mode;
707 
708 struct cnt_item {
709 	struct pmu_counter *pmu;
710 	unsigned int fmt_width;
711 	unsigned int fmt_precision;
712 	double d;
713 	double t;
714 	double s;
715 	const char *name;
716 	const char *unit;
717 
718 	/* Internal fields. */
719 	char buf[16];
720 };
721 
722 struct cnt_group {
723 	const char *name;
724 	const char *display_name;
725 	struct cnt_item *items;
726 };
727 
728 static unsigned int json_indent_level;
729 
730 static const char *json_indent[] = {
731 	"",
732 	"\t",
733 	"\t\t",
734 	"\t\t\t",
735 	"\t\t\t\t",
736 	"\t\t\t\t\t",
737 };
738 
739 #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
740 
741 static unsigned int json_prev_struct_members;
742 static unsigned int json_struct_members;
743 
744 FILE *out;
745 
746 static void
json_open_struct(const char * name)747 json_open_struct(const char *name)
748 {
749 	assert(json_indent_level < ARRAY_SIZE(json_indent));
750 
751 	json_prev_struct_members = json_struct_members;
752 	json_struct_members = 0;
753 
754 	if (name)
755 		fprintf(out, "%s%s\"%s\": {\n",
756 			json_prev_struct_members ? ",\n" : "",
757 			json_indent[json_indent_level],
758 			name);
759 	else
760 		fprintf(out, "%s\n%s{\n",
761 			json_prev_struct_members ? "," : "",
762 			json_indent[json_indent_level]);
763 
764 	json_indent_level++;
765 }
766 
767 static void
json_close_struct(void)768 json_close_struct(void)
769 {
770 	assert(json_indent_level > 0);
771 
772 	fprintf(out, "\n%s}", json_indent[--json_indent_level]);
773 
774 	if (json_indent_level == 0)
775 		fflush(stdout);
776 }
777 
778 static unsigned int
json_add_member(const struct cnt_group * parent,struct cnt_item * item,unsigned int headers)779 json_add_member(const struct cnt_group *parent, struct cnt_item *item,
780 		unsigned int headers)
781 {
782 	assert(json_indent_level < ARRAY_SIZE(json_indent));
783 
784 	fprintf(out, "%s%s\"%s\": ",
785 		json_struct_members ? ",\n" : "",
786 		json_indent[json_indent_level], item->name);
787 
788 	json_struct_members++;
789 
790 	if (!strcmp(item->name, "unit"))
791 		fprintf(out, "\"%s\"", item->unit);
792 	else
793 		fprintf(out, "%f",
794 			pmu_calc(&item->pmu->val, item->d, item->t, item->s));
795 
796 	return 1;
797 }
798 
799 static unsigned int stdout_level;
800 
801 #define STDOUT_HEADER_REPEAT 20
802 static unsigned int stdout_lines = STDOUT_HEADER_REPEAT;
803 
804 static void
stdout_open_struct(const char * name)805 stdout_open_struct(const char *name)
806 {
807 	stdout_level++;
808 	assert(stdout_level > 0);
809 }
810 
811 static void
stdout_close_struct(void)812 stdout_close_struct(void)
813 {
814 	assert(stdout_level > 0);
815 	if (--stdout_level == 0) {
816 		stdout_lines++;
817 		fputs("\n", out);
818 		fflush(out);
819 	}
820 }
821 
822 static unsigned int
stdout_add_member(const struct cnt_group * parent,struct cnt_item * item,unsigned int headers)823 stdout_add_member(const struct cnt_group *parent, struct cnt_item *item,
824 		  unsigned int headers)
825 {
826 	unsigned int fmt_tot = item->fmt_width + (item->fmt_precision ? 1 : 0);
827 	char buf[fmt_tot + 1];
828 	double val;
829 	int len;
830 
831 	if (!item->pmu)
832 		return 0;
833 	else if (!item->pmu->present)
834 		return 0;
835 
836 	if (headers == 1) {
837 		unsigned int grp_tot = 0;
838 		struct cnt_item *it;
839 
840 		if (item != parent->items)
841 			return 0;
842 
843 		for (it = parent->items; it->pmu; it++) {
844 			if (!it->pmu->present)
845 				continue;
846 
847 			grp_tot += 1 + it->fmt_width +
848 				   (it->fmt_precision ? 1 : 0);
849 		}
850 
851 		fprintf(out, "%*s ", grp_tot - 1, parent->display_name);
852 		return 0;
853 	} else if (headers == 2) {
854 		fprintf(out, "%*s ", fmt_tot, item->unit ?: item->name);
855 		return 0;
856 	}
857 
858 	val = pmu_calc(&item->pmu->val, item->d, item->t, item->s);
859 
860 	len = snprintf(buf, sizeof(buf), "%*.*f",
861 		       fmt_tot, item->fmt_precision, val);
862 	if (len < 0 || len == sizeof(buf))
863 		fill_str(buf, sizeof(buf), 'X', fmt_tot);
864 
865 	len = fprintf(out, "%s ", buf);
866 
867 	return len > 0 ? len : 0;
868 }
869 
870 static void
term_open_struct(const char * name)871 term_open_struct(const char *name)
872 {
873 }
874 
875 static void
term_close_struct(void)876 term_close_struct(void)
877 {
878 }
879 
880 static unsigned int
term_add_member(const struct cnt_group * parent,struct cnt_item * item,unsigned int headers)881 term_add_member(const struct cnt_group *parent, struct cnt_item *item,
882 		unsigned int headers)
883 {
884 	unsigned int fmt_tot = item->fmt_width + (item->fmt_precision ? 1 : 0);
885 	double val;
886 	int len;
887 
888 	if (!item->pmu)
889 		return 0;
890 
891 	assert(fmt_tot <= sizeof(item->buf));
892 
893 	if (!item->pmu->present) {
894 		fill_str(item->buf, sizeof(item->buf), '-', fmt_tot);
895 		return 1;
896 	}
897 
898 	val = pmu_calc(&item->pmu->val, item->d, item->t, item->s);
899 	len = snprintf(item->buf, sizeof(item->buf),
900 		       "%*.*f",
901 		       fmt_tot, item->fmt_precision, val);
902 
903 	if (len < 0 || len == sizeof(item->buf))
904 		fill_str(item->buf, sizeof(item->buf), 'X', fmt_tot);
905 
906 	return 1;
907 }
908 
909 struct print_operations {
910 	void (*open_struct)(const char *name);
911 	void (*close_struct)(void);
912 	unsigned int (*add_member)(const struct cnt_group *parent,
913 				   struct cnt_item *item,
914 				   unsigned int headers);
915 	bool (*print_group)(struct cnt_group *group, unsigned int headers);
916 };
917 
918 static const struct print_operations *pops;
919 
920 static unsigned int
present_in_group(const struct cnt_group * grp)921 present_in_group(const struct cnt_group *grp)
922 {
923 	unsigned int present = 0;
924 	struct cnt_item *item;
925 
926 	for (item = grp->items; item->name; item++) {
927 		if (item->pmu && item->pmu->present)
928 			present++;
929 	}
930 
931 	return present;
932 }
933 
934 static bool
print_group(struct cnt_group * grp,unsigned int headers)935 print_group(struct cnt_group *grp, unsigned int headers)
936 {
937 	unsigned int consumed = 0;
938 	struct cnt_item *item;
939 
940 	if (!present_in_group(grp))
941 		return false;
942 
943 	pops->open_struct(grp->name);
944 
945 	for (item = grp->items; item->name; item++)
946 		consumed += pops->add_member(grp, item, headers);
947 
948 	pops->close_struct();
949 
950 	return consumed;
951 }
952 
953 static bool
term_print_group(struct cnt_group * grp,unsigned int headers)954 term_print_group(struct cnt_group *grp, unsigned int headers)
955 {
956 	unsigned int consumed = 0;
957 	struct cnt_item *item;
958 
959 	pops->open_struct(grp->name);
960 
961 	for (item = grp->items; item->name; item++)
962 		consumed += pops->add_member(grp, item, headers);
963 
964 	pops->close_struct();
965 
966 	return consumed;
967 }
968 
969 static const struct print_operations json_pops = {
970 	.open_struct = json_open_struct,
971 	.close_struct = json_close_struct,
972 	.add_member = json_add_member,
973 	.print_group = print_group,
974 };
975 
976 static const struct print_operations stdout_pops = {
977 	.open_struct = stdout_open_struct,
978 	.close_struct = stdout_close_struct,
979 	.add_member = stdout_add_member,
980 	.print_group = print_group,
981 };
982 
983 static const struct print_operations term_pops = {
984 	.open_struct = term_open_struct,
985 	.close_struct = term_close_struct,
986 	.add_member = term_add_member,
987 	.print_group = term_print_group,
988 };
989 
print_groups(struct cnt_group ** groups)990 static bool print_groups(struct cnt_group **groups)
991 {
992 	unsigned int headers = stdout_lines % STDOUT_HEADER_REPEAT + 1;
993 	bool print_data = true;
994 
995 	if (output_mode == STDOUT && (headers == 1 || headers == 2)) {
996 		for (struct cnt_group **grp = groups; *grp; grp++)
997 			print_data = pops->print_group(*grp, headers);
998 	}
999 
1000 	for (struct cnt_group **grp = groups; print_data && *grp; grp++)
1001 		pops->print_group(*grp, false);
1002 
1003 	return print_data;
1004 }
1005 
1006 static int
print_header(struct engines * engines,double t,int lines,int con_w,int con_h,bool * consumed)1007 print_header(struct engines *engines, double t,
1008 	     int lines, int con_w, int con_h, bool *consumed)
1009 {
1010 	struct pmu_counter fake_pmu = {
1011 		.present = true,
1012 		.val.cur = 1,
1013 	};
1014 	struct cnt_item period_items[] = {
1015 		{ &fake_pmu, 0, 0, 1.0, 1.0, t * 1e3, "duration" },
1016 		{ NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "ms" },
1017 		{ },
1018 	};
1019 	struct cnt_group period_group = {
1020 		.name = "period",
1021 		.items = period_items,
1022 	};
1023 	struct cnt_item freq_items[] = {
1024 		{ &engines->freq_req, 4, 0, 1.0, t, 1, "requested", "req" },
1025 		{ &engines->freq_act, 4, 0, 1.0, t, 1, "actual", "act" },
1026 		{ NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "MHz" },
1027 		{ },
1028 	};
1029 	struct cnt_group freq_group = {
1030 		.name = "frequency",
1031 		.display_name = "Freq MHz",
1032 		.items = freq_items,
1033 	};
1034 	struct cnt_item irq_items[] = {
1035 		{ &engines->irq, 8, 0, 1.0, t, 1, "count", "/s" },
1036 		{ NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "irq/s" },
1037 		{ },
1038 	};
1039 	struct cnt_group irq_group = {
1040 		.name = "interrupts",
1041 		.display_name = "IRQ",
1042 		.items = irq_items,
1043 	};
1044 	struct cnt_item rc6_items[] = {
1045 		{ &engines->rc6, 3, 0, 1e9, t, 100, "value", "%" },
1046 		{ NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "%" },
1047 		{ },
1048 	};
1049 	struct cnt_group rc6_group = {
1050 		.name = "rc6",
1051 		.display_name = "RC6",
1052 		.items = rc6_items,
1053 	};
1054 	struct cnt_item power_items[] = {
1055 		{ &engines->rapl, 4, 2, 1.0, t, engines->rapl_scale, "value",
1056 		  "W" },
1057 		{ NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "W" },
1058 		{ },
1059 	};
1060 	struct cnt_group power_group = {
1061 		.name = "power",
1062 		.display_name = "Power",
1063 		.items = power_items,
1064 	};
1065 	struct cnt_group *groups[] = {
1066 		&period_group,
1067 		&freq_group,
1068 		&irq_group,
1069 		&rc6_group,
1070 		&power_group,
1071 		NULL
1072 	};
1073 
1074 	if (output_mode != JSON)
1075 		memmove(&groups[0], &groups[1],
1076 			sizeof(groups) - sizeof(groups[0]));
1077 
1078 	pops->open_struct(NULL);
1079 
1080 	*consumed = print_groups(groups);
1081 
1082 	if (output_mode == INTERACTIVE) {
1083 		printf("\033[H\033[J");
1084 
1085 		if (lines++ < con_h)
1086 			printf("intel-gpu-top - %s/%s MHz;  %s%% RC6; %s %s; %s irqs/s\n",
1087 			       freq_items[1].buf, freq_items[0].buf,
1088 			       rc6_items[0].buf, power_items[0].buf,
1089 			       engines->rapl_unit,
1090 			       irq_items[0].buf);
1091 
1092 		if (lines++ < con_h)
1093 			printf("\n");
1094 	}
1095 
1096 	return lines;
1097 }
1098 
1099 static int
print_imc(struct engines * engines,double t,int lines,int con_w,int con_h)1100 print_imc(struct engines *engines, double t, int lines, int con_w, int con_h)
1101 {
1102 	struct cnt_item imc_items[] = {
1103 		{ &engines->imc_reads, 6, 0, 1.0, t, engines->imc_reads_scale,
1104 		  "reads", "rd" },
1105 		{ &engines->imc_writes, 6, 0, 1.0, t, engines->imc_writes_scale,
1106 		  "writes", "wr" },
1107 		{ NULL, 0, 0, 0.0, 0.0, 0.0, "unit" },
1108 		{ },
1109 	};
1110 	struct cnt_group imc_group = {
1111 		.name = "imc-bandwidth",
1112 		.items = imc_items,
1113 	};
1114 	struct cnt_group *groups[] = {
1115 		&imc_group,
1116 		NULL
1117 	};
1118 	int ret;
1119 
1120 	ret = asprintf((char **)&imc_group.display_name, "IMC %s/s",
1121 			engines->imc_reads_unit);
1122 	assert(ret >= 0);
1123 
1124 	ret = asprintf((char **)&imc_items[2].unit, "%s/s",
1125 			engines->imc_reads_unit);
1126 	assert(ret >= 0);
1127 
1128 	print_groups(groups);
1129 
1130 	free((void *)imc_group.display_name);
1131 	free((void *)imc_items[2].unit);
1132 
1133 	if (output_mode == INTERACTIVE) {
1134 		if (lines++ < con_h)
1135 			printf("      IMC reads:   %s %s/s\n",
1136 			       imc_items[0].buf, engines->imc_reads_unit);
1137 
1138 		if (lines++ < con_h)
1139 			printf("     IMC writes:   %s %s/s\n",
1140 			       imc_items[1].buf, engines->imc_writes_unit);
1141 
1142 		if (lines++ < con_h)
1143 			printf("\n");
1144 	}
1145 
1146 	return lines;
1147 }
1148 
1149 static int
print_engines_header(struct engines * engines,double t,int lines,int con_w,int con_h)1150 print_engines_header(struct engines *engines, double t,
1151 		     int lines, int con_w, int con_h)
1152 {
1153 	for (unsigned int i = 0;
1154 	     i < engines->num_engines && lines < con_h;
1155 	     i++) {
1156 		struct engine *engine = engine_ptr(engines, i);
1157 
1158 		if (!engine->num_counters)
1159 			continue;
1160 
1161 		pops->open_struct("engines");
1162 
1163 		if (output_mode == INTERACTIVE) {
1164 			const char *a = "          ENGINE      BUSY ";
1165 			const char *b = " MI_SEMA MI_WAIT";
1166 
1167 			printf("\033[7m%s%*s%s\033[0m\n",
1168 			       a, (int)(con_w - 1 - strlen(a) - strlen(b)),
1169 			       " ", b);
1170 
1171 			lines++;
1172 		}
1173 
1174 		break;
1175 	}
1176 
1177 	return lines;
1178 }
1179 
1180 static int
print_engine(struct engines * engines,unsigned int i,double t,int lines,int con_w,int con_h)1181 print_engine(struct engines *engines, unsigned int i, double t,
1182 	     int lines, int con_w, int con_h)
1183 {
1184 	struct engine *engine = engine_ptr(engines, i);
1185 	struct cnt_item engine_items[] = {
1186 		{ &engine->busy, 6, 2, 1e9, t, 100, "busy", "%" },
1187 		{ &engine->sema, 3, 0, 1e9, t, 100, "sema", "se" },
1188 		{ &engine->wait, 3, 0, 1e9, t, 100, "wait", "wa" },
1189 		{ NULL, 0, 0, 0.0, 0.0, 0.0, "unit", "%" },
1190 		{ },
1191 	};
1192 	struct cnt_group engine_group = {
1193 		.name = engine->display_name,
1194 		.display_name = engine->short_name,
1195 		.items = engine_items,
1196 	};
1197 	struct cnt_group *groups[] = {
1198 		&engine_group,
1199 		NULL
1200 	};
1201 
1202 	if (!engine->num_counters)
1203 		return lines;
1204 
1205 	print_groups(groups);
1206 
1207 	if (output_mode == INTERACTIVE) {
1208 		unsigned int max_w = con_w - 1;
1209 		unsigned int len;
1210 		char buf[128];
1211 		double val;
1212 
1213 		len = snprintf(buf, sizeof(buf), "    %s%%    %s%%",
1214 			       engine_items[1].buf, engine_items[2].buf);
1215 
1216 		len += printf("%16s %s%% ",
1217 			      engine->display_name, engine_items[0].buf);
1218 
1219 		val = pmu_calc(&engine->busy.val, 1e9, t, 100);
1220 		print_percentage_bar(val, max_w - len);
1221 
1222 		printf("%s\n", buf);
1223 
1224 		lines++;
1225 	}
1226 
1227 	return lines;
1228 }
1229 
1230 static int
print_engines_footer(struct engines * engines,double t,int lines,int con_w,int con_h)1231 print_engines_footer(struct engines *engines, double t,
1232 		     int lines, int con_w, int con_h)
1233 {
1234 	pops->close_struct();
1235 	pops->close_struct();
1236 
1237 	if (output_mode == INTERACTIVE) {
1238 		if (lines++ < con_h)
1239 			printf("\n");
1240 	}
1241 
1242 	return lines;
1243 }
1244 
1245 static bool stop_top;
1246 
sigint_handler(int sig)1247 static void sigint_handler(int  sig)
1248 {
1249 	stop_top = true;
1250 }
1251 
main(int argc,char ** argv)1252 int main(int argc, char **argv)
1253 {
1254 	unsigned int period_us = DEFAULT_PERIOD_MS * 1000;
1255 	int con_w = -1, con_h = -1;
1256 	char *output_path = NULL;
1257 	struct engines *engines;
1258 	unsigned int i;
1259 	int ret, ch;
1260 
1261 	/* Parse options */
1262 	while ((ch = getopt(argc, argv, "o:s:Jlh")) != -1) {
1263 		switch (ch) {
1264 		case 'o':
1265 			output_path = optarg;
1266 			break;
1267 		case 's':
1268 			period_us = atoi(optarg) * 1000;
1269 			break;
1270 		case 'J':
1271 			output_mode = JSON;
1272 			break;
1273 		case 'l':
1274 			output_mode = STDOUT;
1275 			break;
1276 		case 'h':
1277 			usage(argv[0]);
1278 			exit(0);
1279 		default:
1280 			fprintf(stderr, "Invalid option %c!\n", (char)optopt);
1281 			usage(argv[0]);
1282 			exit(1);
1283 		}
1284 	}
1285 
1286 	if (output_mode == INTERACTIVE && (output_path || isatty(1) != 1))
1287 		output_mode = STDOUT;
1288 
1289 	if (output_path && strcmp(output_path, "-")) {
1290 		out = fopen(output_path, "w");
1291 
1292 		if (!out) {
1293 			fprintf(stderr, "Failed to open output file - '%s'!\n",
1294 				strerror(errno));
1295 			exit(1);
1296 		}
1297 	} else {
1298 		out = stdout;
1299 	}
1300 
1301 	if (output_mode != INTERACTIVE) {
1302 		sighandler_t sig = signal(SIGINT, sigint_handler);
1303 
1304 		if (sig == SIG_ERR)
1305 			fprintf(stderr, "Failed to install signal handler!\n");
1306 	}
1307 
1308 	switch (output_mode) {
1309 	case INTERACTIVE:
1310 		pops = &term_pops;
1311 		break;
1312 	case STDOUT:
1313 		pops = &stdout_pops;
1314 		break;
1315 	case JSON:
1316 		pops = &json_pops;
1317 		break;
1318 	default:
1319 		assert(0);
1320 		break;
1321 	};
1322 
1323 	engines = discover_engines();
1324 	if (!engines) {
1325 		fprintf(stderr,
1326 			"Failed to detect engines! (%s)\n(Kernel 4.16 or newer is required for i915 PMU support.)\n",
1327 			strerror(errno));
1328 		return 1;
1329 	}
1330 
1331 	ret = pmu_init(engines);
1332 	if (ret) {
1333 		fprintf(stderr,
1334 			"Failed to initialize PMU! (%s)\n", strerror(errno));
1335 		return 1;
1336 	}
1337 
1338 	pmu_sample(engines);
1339 
1340 	while (!stop_top) {
1341 		bool consumed = false;
1342 		int lines = 0;
1343 		struct winsize ws;
1344 		double t;
1345 
1346 		/* Update terminal size. */
1347 		if (output_mode != INTERACTIVE) {
1348 			con_w = con_h = INT_MAX;
1349 		} else if (ioctl(0, TIOCGWINSZ, &ws) != -1) {
1350 			con_w = ws.ws_col;
1351 			con_h = ws.ws_row;
1352 		}
1353 
1354 		pmu_sample(engines);
1355 		t = (double)(engines->ts.cur - engines->ts.prev) / 1e9;
1356 
1357 		if (stop_top)
1358 			break;
1359 
1360 		while (!consumed) {
1361 			lines = print_header(engines, t, lines, con_w, con_h,
1362 					     &consumed);
1363 
1364 			if (engines->imc_fd)
1365 				lines = print_imc(engines, t, lines, con_w,
1366 						  con_h);
1367 
1368 			lines = print_engines_header(engines, t, lines, con_w,
1369 						     con_h);
1370 
1371 			for (i = 0;
1372 			     i < engines->num_engines && lines < con_h;
1373 			     i++)
1374 				lines = print_engine(engines, i, t, lines,
1375 						     con_w, con_h);
1376 
1377 			lines = print_engines_footer(engines, t, lines, con_w,
1378 						     con_h);
1379 		}
1380 
1381 		if (stop_top)
1382 			break;
1383 
1384 		usleep(period_us);
1385 	}
1386 
1387 	return 0;
1388 }
1389