xref: /aosp_15_r20/external/igt-gpu-tools/overlay/overlay.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1 /*
2  * Copyright © 2013 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 DEALINGS
21  * IN THE SOFTWARE.
22  *
23  */
24 
25 #include <sys/types.h>
26 #include <sys/mman.h>
27 #include <cairo.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdbool.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <signal.h>
36 #include <getopt.h>
37 #include <time.h>
38 #include <locale.h>
39 
40 #include "overlay.h"
41 #include "chart.h"
42 #include "config.h"
43 #include "cpu-top.h"
44 #include "debugfs.h"
45 #include "gem-interrupts.h"
46 #include "gem-objects.h"
47 #include "gpu-freq.h"
48 #include "gpu-top.h"
49 #include "gpu-perf.h"
50 #include "power.h"
51 #include "rc6.h"
52 
53 #define is_power_of_two(x)  (((x) & ((x)-1)) == 0)
54 
55 #define PAD 10
56 #define HALF_PAD 5
57 #define SIZE_PAD (PAD + HALF_PAD)
58 
59 #define IDLE_TIME 30
60 
61 const cairo_user_data_key_t overlay_key;
62 
overlay_show(cairo_surface_t * surface)63 static void overlay_show(cairo_surface_t *surface)
64 {
65 	struct overlay *overlay;
66 
67 	overlay = cairo_surface_get_user_data(surface, &overlay_key);
68 	if (overlay == NULL)
69 		return;
70 
71 	overlay->show(overlay);
72 }
73 
74 #if 0
75 static void overlay_position(cairo_surface_t *surface, enum position p)
76 {
77 	struct overlay *overlay;
78 
79 	overlay = cairo_surface_get_user_data(surface, &overlay_key);
80 	if (overlay == NULL)
81 		return;
82 
83 	overlay->position(overlay, p);
84 }
85 
86 static void overlay_hide(cairo_surface_t *surface)
87 {
88 	struct overlay *overlay;
89 
90 	overlay = cairo_surface_get_user_data(surface, &overlay_key);
91 	if (overlay == NULL)
92 		return;
93 
94 	overlay->hide(overlay);
95 }
96 #endif
97 
98 struct overlay_gpu_top {
99 	struct gpu_top gpu_top;
100 	struct cpu_top cpu_top;
101 	struct chart busy[MAX_RINGS];
102 	struct chart wait[MAX_RINGS];
103 	struct chart cpu;
104 };
105 
106 struct overlay_gpu_perf {
107 	struct gpu_perf gpu_perf;
108 	time_t show_ctx;
109 	time_t show_flips;
110 };
111 
112 struct overlay_gpu_freq {
113 	struct gpu_freq gpu_freq;
114 	struct rc6 rc6;
115 	struct gem_interrupts irqs;
116 	struct power power;
117 	struct chart current;
118 	struct chart request;
119 	struct chart power_chart;
120 	double power_max;
121 };
122 
123 struct overlay_gem_objects {
124 	struct gem_objects gem_objects;
125 	struct chart aperture;
126 	struct chart gtt;
127 	int error;
128 };
129 
130 struct overlay_context {
131 	cairo_surface_t *surface;
132 	cairo_t *cr;
133 	int width, height;
134 
135 	time_t time;
136 
137 	struct overlay_gpu_top gpu_top;
138 	struct overlay_gpu_perf gpu_perf;
139 	struct overlay_gpu_freq gpu_freq;
140 	struct overlay_gem_objects gem_objects;
141 };
142 
init_gpu_top(struct overlay_context * ctx,struct overlay_gpu_top * gt)143 static void init_gpu_top(struct overlay_context *ctx,
144 			 struct overlay_gpu_top *gt)
145 {
146 	const double rgba[][4] = {
147 		{ 1, 0.25, 0.25, 1 },
148 		{ 0.25, 1, 0.25, 1 },
149 		{ 0.25, 0.25, 1, 1 },
150 		{ 1, 1, 1, 1 },
151 		{ 1, 1, 0.25, 1 },
152 	};
153 	int n;
154 
155 	cpu_top_init(&gt->cpu_top);
156 	gpu_top_init(&gt->gpu_top);
157 
158 	chart_init(&gt->cpu, "CPU", 120);
159 	chart_set_position(&gt->cpu, PAD, PAD);
160 	chart_set_size(&gt->cpu, ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
161 	chart_set_stroke_rgba(&gt->cpu, 0.75, 0.25, 0.75, 1.);
162 	chart_set_mode(&gt->cpu, CHART_STROKE);
163 	chart_set_range(&gt->cpu, 0, 100);
164 
165 	for (n = 0; n < gt->gpu_top.num_rings; n++) {
166 		chart_init(&gt->busy[n],
167 			   gt->gpu_top.ring[n].name,
168 			   120);
169 		chart_set_position(&gt->busy[n], PAD, PAD);
170 		chart_set_size(&gt->busy[n], ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
171 		chart_set_stroke_rgba(&gt->busy[n],
172 				    rgba[n][0], rgba[n][1], rgba[n][2], rgba[n][3]);
173 		chart_set_mode(&gt->busy[n], CHART_STROKE);
174 		chart_set_range(&gt->busy[n], 0, 100);
175 	}
176 
177 	for (n = 0; n < gt->gpu_top.num_rings; n++) {
178 		chart_init(&gt->wait[n],
179 			   gt->gpu_top.ring[n].name,
180 			   120);
181 		chart_set_position(&gt->wait[n], PAD, PAD);
182 		chart_set_size(&gt->wait[n], ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
183 		chart_set_fill_rgba(&gt->wait[n],
184 				    rgba[n][0], rgba[n][1], rgba[n][2], rgba[n][3] * 0.70);
185 		chart_set_mode(&gt->wait[n], CHART_FILL);
186 		chart_set_range(&gt->wait[n], 0, 100);
187 	}
188 }
189 
show_gpu_top(struct overlay_context * ctx,struct overlay_gpu_top * gt)190 static void show_gpu_top(struct overlay_context *ctx, struct overlay_gpu_top *gt)
191 {
192 	int y, y1, y2, n, update, len;
193 	cairo_pattern_t *linear;
194 	char txt[160];
195 	int rewind;
196 	int do_rewind;
197 
198 	update = gpu_top_update(&gt->gpu_top);
199 
200 	cairo_rectangle(ctx->cr, PAD-.5, PAD-.5, ctx->width/2-SIZE_PAD+1, ctx->height/2-SIZE_PAD+1);
201 	cairo_set_source_rgb(ctx->cr, .15, .15, .15);
202 	cairo_set_line_width(ctx->cr, 1);
203 	cairo_stroke(ctx->cr);
204 
205 	if (update && cpu_top_update(&gt->cpu_top) == 0)
206 		chart_add_sample(&gt->cpu, gt->cpu_top.busy);
207 
208 	for (n = 0; n < gt->gpu_top.num_rings; n++) {
209 		if (update)
210 			chart_add_sample(&gt->wait[n],
211 					 gt->gpu_top.ring[n].u.u.wait + gt->gpu_top.ring[n].u.u.sema);
212 		chart_draw(&gt->wait[n], ctx->cr);
213 	}
214 	for (n = 0; n < gt->gpu_top.num_rings; n++) {
215 		if (update)
216 			chart_add_sample(&gt->busy[n],
217 					 gt->gpu_top.ring[n].u.u.busy);
218 		chart_draw(&gt->busy[n], ctx->cr);
219 	}
220 	chart_draw(&gt->cpu, ctx->cr);
221 
222 	y1 = PAD - 2;
223 	y2 = y1 + (gt->gpu_top.num_rings+1) * 14 + 4;
224 
225 	cairo_rectangle(ctx->cr, PAD, y1, ctx->width/2-SIZE_PAD, y2-y1);
226 	linear = cairo_pattern_create_linear(PAD, 0, PAD+ctx->width/2-SIZE_PAD, 0);
227 	cairo_pattern_add_color_stop_rgba(linear, 0, 0, 0, 0, .5);
228 	cairo_pattern_add_color_stop_rgba(linear, 1, 0, 0, 0, .0);
229 	cairo_set_source(ctx->cr, linear);
230 	cairo_pattern_destroy(linear);
231 	cairo_fill(ctx->cr);
232 
233 	y = PAD + 12 - 2;
234 	cairo_set_source_rgba(ctx->cr, 0.75, 0.25, 0.75, 1.);
235 	cairo_move_to(ctx->cr, PAD, y);
236 	rewind = len = sprintf(txt, "CPU: %3d%% busy", gt->cpu_top.busy * gt->cpu_top.nr_cpu);
237 	do_rewind = 1;
238 	len += sprintf(txt + len, " (");
239 	if (gt->cpu_top.nr_cpu > 1) {
240 		len += sprintf(txt + len, "%s%d cores", do_rewind ? "" : ", ", gt->cpu_top.nr_cpu);
241 		do_rewind = 0;
242 	}
243 	if (gt->cpu_top.nr_running) {
244 		len += sprintf(txt + len, "%s%d processes", do_rewind ? "" : ", ", gt->cpu_top.nr_running);
245 		do_rewind = 0;
246 	}
247 	sprintf(txt + len, ")");
248 	if (do_rewind)
249 		txt[rewind] = '\0';
250 	cairo_show_text(ctx->cr, txt);
251 	y += 14;
252 
253 	for (n = 0; n < gt->gpu_top.num_rings; n++) {
254 		struct chart *c =&gt->busy[n];
255 
256 		len = sprintf(txt, "%s: %3d%% busy",
257 			      gt->gpu_top.ring[n].name,
258 			      gt->gpu_top.ring[n].u.u.busy);
259 		if (gt->gpu_top.ring[n].u.u.wait)
260 			len += sprintf(txt + len, ", %d%% wait",
261 				       gt->gpu_top.ring[n].u.u.wait);
262 		if (gt->gpu_top.ring[n].u.u.sema)
263 			len += sprintf(txt + len, ", %d%% sema",
264 				       gt->gpu_top.ring[n].u.u.sema);
265 
266 		cairo_set_source_rgba(ctx->cr,
267 				      c->stroke_rgb[0],
268 				      c->stroke_rgb[1],
269 				      c->stroke_rgb[2],
270 				      c->stroke_rgb[3]);
271 		cairo_move_to(ctx->cr, PAD, y);
272 		cairo_show_text(ctx->cr, txt);
273 		y += 14;
274 	}
275 }
276 
init_gpu_perf(struct overlay_context * ctx,struct overlay_gpu_perf * gp)277 static void init_gpu_perf(struct overlay_context *ctx,
278 			  struct overlay_gpu_perf *gp)
279 {
280 	gpu_perf_init(&gp->gpu_perf, 0);
281 
282 	gp->show_ctx = 0;
283 	gp->show_flips = 0;
284 }
285 
get_comm(pid_t pid,char * comm,int len)286 static char *get_comm(pid_t pid, char *comm, int len)
287 {
288 	char filename[1024];
289 	int fd;
290 
291 	*comm = '\0';
292 	snprintf(filename, sizeof(filename), "/proc/%d/comm", pid);
293 
294 	fd = open(filename, 0);
295 	if (fd >= 0) {
296 		len = read(fd, comm, len);
297 		if (len >= 0)
298 			comm[len-1] = '\0';
299 		close(fd);
300 	}
301 
302 	return comm;
303 }
304 
show_gpu_perf(struct overlay_context * ctx,struct overlay_gpu_perf * gp)305 static void show_gpu_perf(struct overlay_context *ctx, struct overlay_gpu_perf *gp)
306 {
307 	static int last_color;
308 	const double rgba[][4] = {
309 		{ 1, 0.25, 0.25, 1 },
310 		{ 0.25, 1, 0.25, 1 },
311 		{ 0.25, 0.25, 1, 1 },
312 		{ 1, 1, 1, 1 },
313 	};
314 	struct gpu_perf_comm *comm, **prev;
315 	const char *ring_name[MAX_RINGS] = {
316 		"R", "?", "?", "?",
317 		"B", "?", "?", "?",
318 		"V0", "V1", "?", "?",
319 		"VE0", "?", "?", "?",
320 	};
321 	double range[2];
322 	char buf[1024];
323 	cairo_pattern_t *linear;
324 	int x, y, y1, y2, n;
325 	int has_ctx = 0;
326 	int has_flips = 0;
327 
328 	gpu_perf_update(&gp->gpu_perf);
329 
330 	for (n = 0; n < MAX_RINGS; n++) {
331 		if (gp->gpu_perf.ctx_switch[n])
332 			has_ctx = n + 1;
333 		if (gp->gpu_perf.flip_complete[n])
334 			has_flips = n + 1;
335 	}
336 
337 	cairo_rectangle(ctx->cr, ctx->width/2+HALF_PAD-.5, PAD-.5, ctx->width/2-SIZE_PAD+1, ctx->height/2-SIZE_PAD+1);
338 	cairo_set_source_rgb(ctx->cr, .15, .15, .15);
339 	cairo_set_line_width(ctx->cr, 1);
340 	cairo_stroke(ctx->cr);
341 
342 	if (gp->gpu_perf.error) {
343 		cairo_text_extents_t extents;
344 		cairo_text_extents(ctx->cr, gp->gpu_perf.error, &extents);
345 		cairo_move_to(ctx->cr,
346 			      ctx->width/2+HALF_PAD + (ctx->width/2-SIZE_PAD - extents.width)/2.,
347 			      PAD + (ctx->height/2-SIZE_PAD + extents.height)/2.);
348 		cairo_show_text(ctx->cr, gp->gpu_perf.error);
349 		return;
350 	}
351 
352 	if (gp->gpu_perf.comm == NULL && (has_ctx|has_flips) == 0) {
353 		cairo_text_extents_t extents;
354 		cairo_text_extents(ctx->cr, gp->gpu_perf.error, &extents);
355 		cairo_move_to(ctx->cr,
356 			      ctx->width/2+HALF_PAD + (ctx->width/2-SIZE_PAD - extents.width)/2.,
357 			      PAD + (ctx->height/2-SIZE_PAD + extents.height)/2.);
358 		cairo_show_text(ctx->cr, "idle");
359 		return;
360 	}
361 
362 	y = PAD + 12 - 2;
363 	x = ctx->width/2 + HALF_PAD;
364 
365 	for (comm = gp->gpu_perf.comm; comm; comm = comm->next) {
366 		int total;
367 
368 		if (comm->name[0] == '\0')
369 			continue;
370 
371 		if (strncmp(comm->name, "kworker", 7) == 0)
372 			continue;
373 
374 		if (comm->user_data == NULL) {
375 			comm->user_data = malloc(sizeof(struct chart));
376 			if (comm->user_data == NULL)
377 				continue;
378 
379 			chart_init(comm->user_data, comm->name, 120);
380 			chart_set_position(comm->user_data, ctx->width/2+HALF_PAD, PAD);
381 			chart_set_size(comm->user_data, ctx->width/2-SIZE_PAD, ctx->height/2 - SIZE_PAD);
382 			chart_set_mode(comm->user_data, CHART_STROKE);
383 			chart_set_stroke_rgba(comm->user_data,
384 					      rgba[last_color][0],
385 					      rgba[last_color][1],
386 					      rgba[last_color][2],
387 					      rgba[last_color][3]);
388 			last_color = (last_color + 1) % 4;
389 			chart_set_stroke_width(comm->user_data, 1);
390 		}
391 
392 		total = 0;
393 		for (n = 0; n < MAX_RINGS; n++)
394 			total += comm->nr_requests[n];
395 		chart_add_sample(comm->user_data, total);
396 	}
397 
398 	range[0] = range[1] = 0;
399 	for (comm = gp->gpu_perf.comm; comm; comm = comm->next) {
400 		if (comm->user_data == NULL)
401 			continue;
402 
403 		chart_get_range(comm->user_data, range);
404 	}
405 
406 	y2 = y1 = y;
407 	for (comm = gp->gpu_perf.comm; comm; comm = comm->next) {
408 		if (comm->user_data == NULL)
409 			continue;
410 
411 		chart_set_range(comm->user_data, range[0], range[1]);
412 		chart_draw(comm->user_data, ctx->cr);
413 		y2 += 14;
414 	}
415 	if (has_flips || gp->show_flips)
416 		y2 += 14;
417 	if (has_ctx || gp->show_ctx)
418 		y2 += 14;
419 	y1 += -12 - 2;
420 	y2 += -14 + 4;
421 
422 	cairo_rectangle(ctx->cr, x, y1, ctx->width/2-SIZE_PAD, y2-y1);
423 	linear = cairo_pattern_create_linear(x, 0, x + ctx->width/2-SIZE_PAD, 0);
424 	cairo_pattern_add_color_stop_rgba(linear, 0, 0, 0, 0, .5);
425 	cairo_pattern_add_color_stop_rgba(linear, 1, 0, 0, 0, .0);
426 	cairo_set_source(ctx->cr, linear);
427 	cairo_pattern_destroy(linear);
428 	cairo_fill(ctx->cr);
429 
430 	for (prev = &gp->gpu_perf.comm; (comm = *prev) != NULL; ) {
431 		int need_comma = 0, len;
432 
433 		if (comm->user_data == NULL)
434 			goto skip_comm;
435 
436 		len = sprintf(buf, "%s:", comm->name);
437 		for (n = 0; n < MAX_RINGS; n++) {
438 			if (comm->nr_requests[n] == 0)
439 				continue;
440 			len += sprintf(buf + len, "%s %d%s", need_comma ? "," : "", comm->nr_requests[n], ring_name[n]);
441 			need_comma = true;
442 			comm->show = ctx->time;
443 		}
444 		if (comm->wait_time) {
445 			if (comm->wait_time > 1000*1000) {
446 				len += sprintf(buf + len, "%s %.1fms waits",
447 					       need_comma ? "," : "",
448 					       comm->wait_time / (1000*1000.));
449 			} else if (comm->wait_time > 100) {
450 				len += sprintf(buf + len, "%s %.1fus waits",
451 					       need_comma ? "," : "",
452 					       comm->wait_time / 1000.);
453 			} else {
454 				len += sprintf(buf, "%s %.0fns waits",
455 					       need_comma ? "," : "",
456 					       (double)comm->wait_time);
457 			}
458 			need_comma = true;
459 			comm->wait_time = 0;
460 			comm->show = ctx->time;
461 		}
462 		if (comm->nr_sema) {
463 			len += sprintf(buf + len, "%s %d syncs",
464 				       need_comma ? "," : "",
465 				       comm->nr_sema);
466 			need_comma = true;
467 			comm->nr_sema = 0;
468 			comm->show = ctx->time;
469 		}
470 
471 		if (comm->user_data) {
472 			struct chart *c = comm->user_data;
473 			cairo_set_source_rgba(ctx->cr,
474 					      c->stroke_rgb[0],
475 					      c->stroke_rgb[1],
476 					      c->stroke_rgb[2],
477 					      c->stroke_rgb[3]);
478 		} else
479 			cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
480 		cairo_move_to(ctx->cr, x, y);
481 		cairo_show_text(ctx->cr, buf);
482 		y += 14;
483 
484 skip_comm:
485 		memset(comm->nr_requests, 0, sizeof(comm->nr_requests));
486 		if (!comm->active &&
487 		    (comm->show < ctx->time - IDLE_TIME ||
488 		     strcmp(comm->name, get_comm(comm->pid, buf, sizeof(buf))))) {
489 			*prev = comm->next;
490 			if (comm->user_data) {
491 				chart_fini(comm->user_data);
492 				free(comm->user_data);
493 			}
494 			free(comm);
495 		} else
496 			prev = &comm->next;
497 	}
498 
499 	cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
500 	cairo_move_to(ctx->cr, x, y);
501 	if (has_flips) {
502 		int len = sprintf(buf, "Flips:");
503 		for (n = 0; n < has_flips; n++)
504 			len += sprintf(buf + len, "%s %d",
505 				       n ? "," : "",
506 				       gp->gpu_perf.flip_complete[n]);
507 		memset(gp->gpu_perf.flip_complete, 0, sizeof(gp->gpu_perf.flip_complete));
508 		gp->show_flips = ctx->time;
509 
510 		cairo_show_text(ctx->cr, buf);
511 		y += 14;
512 	} else if (gp->show_flips) {
513 		cairo_show_text(ctx->cr, "Flips: 0");
514 		if (ctx->time - gp->show_flips > IDLE_TIME)
515 			gp->show_flips = 0;
516 		y += 14;
517 	}
518 
519 	cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
520 	cairo_move_to(ctx->cr, x, y);
521 	if (has_ctx) {
522 		int len = sprintf(buf, "Contexts:");
523 		for (n = 0; n < has_ctx; n++)
524 			len += sprintf(buf + len, "%s %d",
525 				       n ? "," : "",
526 				       gp->gpu_perf.ctx_switch[n]);
527 
528 		memset(gp->gpu_perf.ctx_switch, 0, sizeof(gp->gpu_perf.ctx_switch));
529 		gp->show_ctx = ctx->time;
530 
531 		cairo_show_text(ctx->cr, buf);
532 		y += 14;
533 	} else if (gp->show_ctx) {
534 		cairo_show_text(ctx->cr, "Contexts: 0");
535 		y += 14;
536 		if (ctx->time - gp->show_ctx > IDLE_TIME)
537 			gp->show_ctx = 0;
538 	}
539 }
540 
init_gpu_freq(struct overlay_context * ctx,struct overlay_gpu_freq * gf)541 static void init_gpu_freq(struct overlay_context *ctx,
542 			  struct overlay_gpu_freq *gf)
543 {
544 	if (gpu_freq_init(&gf->gpu_freq) == 0) {
545 		chart_init(&gf->current, "current", 120);
546 		chart_set_position(&gf->current, PAD, ctx->height/2 + HALF_PAD);
547 		chart_set_size(&gf->current, ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
548 		chart_set_stroke_rgba(&gf->current, 0.75, 0.25, 0.50, 1.);
549 		chart_set_mode(&gf->current, CHART_STROKE);
550 		chart_set_smooth(&gf->current, CHART_LINE);
551 		chart_set_range(&gf->current, 0, gf->gpu_freq.max);
552 
553 		chart_init(&gf->request, "request", 120);
554 		chart_set_position(&gf->request, PAD, ctx->height/2 + HALF_PAD);
555 		chart_set_size(&gf->request, ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
556 		chart_set_fill_rgba(&gf->request, 0.25, 0.25, 0.50, 1.);
557 		chart_set_mode(&gf->request, CHART_FILL);
558 		chart_set_smooth(&gf->request, CHART_LINE);
559 		chart_set_range(&gf->request, 0, gf->gpu_freq.max);
560 	}
561 
562 	if (power_init(&gf->power) == 0) {
563 		chart_init(&gf->power_chart, "power", 120);
564 		chart_set_position(&gf->power_chart, PAD, ctx->height/2 + HALF_PAD);
565 		chart_set_size(&gf->power_chart, ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
566 		chart_set_stroke_rgba(&gf->power_chart, 0.45, 0.55, 0.45, 1.);
567 		gf->power_max = 0;
568 	}
569 
570 	rc6_init(&gf->rc6);
571 	gem_interrupts_init(&gf->irqs);
572 }
573 
show_gpu_freq(struct overlay_context * ctx,struct overlay_gpu_freq * gf)574 static void show_gpu_freq(struct overlay_context *ctx, struct overlay_gpu_freq *gf)
575 {
576 	char buf[160];
577 	int y1, y2, y, len;
578 
579 	int has_freq = gpu_freq_update(&gf->gpu_freq) == 0;
580 	int has_rc6 = rc6_update(&gf->rc6) == 0;
581 	int has_power = power_update(&gf->power) == 0;
582 	int has_irqs = gem_interrupts_update(&gf->irqs) == 0;
583 	cairo_pattern_t *linear;
584 
585 	cairo_rectangle(ctx->cr, PAD-.5, ctx->height/2+HALF_PAD-.5, ctx->width/2-SIZE_PAD+1, ctx->height/2-SIZE_PAD+1);
586 	cairo_set_source_rgb(ctx->cr, .15, .15, .15);
587 	cairo_set_line_width(ctx->cr, 1);
588 	cairo_stroke(ctx->cr);
589 
590 	if (gf->gpu_freq.error) {
591 		const char *txt = "GPU frequency not found in debugfs";
592 		cairo_text_extents_t extents;
593 		cairo_text_extents(ctx->cr, txt, &extents);
594 		cairo_move_to(ctx->cr,
595 			      PAD + (ctx->width/2-SIZE_PAD - extents.width)/2.,
596 			      ctx->height/2+HALF_PAD + (ctx->height/2-SIZE_PAD + extents.height)/2.);
597 		cairo_show_text(ctx->cr, txt);
598 		return;
599 	}
600 
601 	if (has_freq) {
602 		if (gf->gpu_freq.current)
603 			chart_add_sample(&gf->current, gf->gpu_freq.current);
604 		if (gf->gpu_freq.request)
605 			chart_add_sample(&gf->request, gf->gpu_freq.request);
606 
607 		chart_draw(&gf->request, ctx->cr);
608 		chart_draw(&gf->current, ctx->cr);
609 	}
610 
611 	if (has_power) {
612 		chart_add_sample(&gf->power_chart, gf->power.power_mW);
613 		if (gf->power.new_sample) {
614 			if (gf->power.power_mW > gf->power_max)
615 				gf->power_max = gf->power.power_mW;
616 			chart_set_range(&gf->power_chart, 0, gf->power_max);
617 			gf->power.new_sample = 0;
618 		}
619 		chart_draw(&gf->power_chart, ctx->cr);
620 	}
621 
622 	y = ctx->height/2 + HALF_PAD + 12 - 2;
623 
624 	y1 = y2 = y;
625 	if (has_freq) {
626 		y2 += 12;
627 		y2 += 12;
628 	}
629 	if (has_rc6)
630 		y2 += 14;
631 	if (has_power)
632 		y2 += 14;
633 	if (has_irqs)
634 		y2 += 14;
635 	y1 += -12 - 2;
636 	y2 += -14 + 4;
637 
638 	cairo_rectangle(ctx->cr, PAD, y1, ctx->width/2-SIZE_PAD, y2-y1);
639 	linear = cairo_pattern_create_linear(PAD, 0, PAD+ctx->width/2-SIZE_PAD, 0);
640 	cairo_pattern_add_color_stop_rgba(linear, 0, 0, 0, 0, .5);
641 	cairo_pattern_add_color_stop_rgba(linear, 1, 0, 0, 0, .0);
642 	cairo_set_source(ctx->cr, linear);
643 	cairo_pattern_destroy(linear);
644 	cairo_fill(ctx->cr);
645 
646 	if (has_freq) {
647 		cairo_text_extents_t extents;
648 
649 		len = sprintf(buf, "Frequency: %dMHz", gf->gpu_freq.current);
650 		if (gf->gpu_freq.request)
651 		cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
652 			sprintf(buf + len, " (requested %dMHz)", gf->gpu_freq.request);
653 		cairo_move_to(ctx->cr, PAD, y);
654 		cairo_show_text(ctx->cr, buf);
655 		y += 12;
656 
657 		cairo_text_extents(ctx->cr, "Frequency: ", &extents);
658 
659 		cairo_set_font_size(ctx->cr, 8);
660 		sprintf(buf, " min: %dMHz, max: %dMHz", gf->gpu_freq.min, gf->gpu_freq.max);
661 		cairo_set_source_rgba(ctx->cr, .8, .8, .8, 1);
662 		cairo_move_to(ctx->cr, PAD + extents.width, y);
663 		cairo_show_text(ctx->cr, buf);
664 		cairo_set_font_size(ctx->cr, 10);
665 		y += 12;
666 	}
667 
668 	if (has_rc6) {
669 		len = sprintf(buf, "RC6: %d%%", gf->rc6.rc6_combined);
670 		cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
671 		cairo_move_to(ctx->cr, PAD, y);
672 		if (gf->rc6.rc6_combined) {
673 			int need_comma = 0;
674 			int rewind = len;
675 			len += sprintf(buf + len, " (");
676 			if (gf->rc6.rc6) {
677 				len += sprintf(buf + len, "%src6=%d%%",
678 					       need_comma ? ", " : "",
679 					       gf->rc6.rc6);
680 				need_comma++;
681 			}
682 			if (gf->rc6.rc6p) {
683 				len += sprintf(buf + len, "%src6p=%d%%",
684 					       need_comma ? ", " : "",
685 					       gf->rc6.rc6p);
686 				need_comma++;
687 			}
688 			if (gf->rc6.rc6pp) {
689 				len += sprintf(buf + len, "%src6pp=%d%%",
690 					       need_comma ? ", " : "",
691 					       gf->rc6.rc6pp);
692 				need_comma++;
693 			}
694 			sprintf(buf + len, ")");
695 			if (need_comma <= 1)
696 				buf[rewind] = '\0';
697 		}
698 		cairo_show_text(ctx->cr, buf);
699 		y += 14;
700 	}
701 
702 	if (has_power) {
703 		sprintf(buf, "Power: %llumW", (long long unsigned)gf->power.power_mW);
704 		cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
705 		cairo_move_to(ctx->cr, PAD, y);
706 		cairo_show_text(ctx->cr, buf);
707 		y += 14;
708 	}
709 
710 	if (has_irqs) {
711 		sprintf(buf, "Interrupts: %llu", (long long unsigned)gf->irqs.delta);
712 		cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
713 		cairo_move_to(ctx->cr, PAD, y);
714 		cairo_show_text(ctx->cr, buf);
715 		y += 14;
716 	}
717 }
718 
init_gem_objects(struct overlay_context * ctx,struct overlay_gem_objects * go)719 static void init_gem_objects(struct overlay_context *ctx,
720 			     struct overlay_gem_objects *go)
721 {
722 	go->error = gem_objects_init(&go->gem_objects);
723 	if (go->error)
724 		return;
725 
726 	chart_init(&go->aperture, "aperture", 120);
727 	chart_set_position(&go->aperture, ctx->width/2+HALF_PAD, ctx->height/2 + HALF_PAD);
728 	chart_set_size(&go->aperture, ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
729 	chart_set_stroke_rgba(&go->aperture, 0.75, 0.25, 0.50, 1.);
730 	chart_set_mode(&go->aperture, CHART_STROKE);
731 	chart_set_range(&go->aperture, 0, go->gem_objects.max_gtt);
732 
733 	chart_init(&go->gtt, "gtt", 120);
734 	chart_set_position(&go->gtt, ctx->width/2+HALF_PAD, ctx->height/2 + HALF_PAD);
735 	chart_set_size(&go->gtt, ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
736 	chart_set_fill_rgba(&go->gtt, 0.25, 0.5, 0.5, 1.);
737 	chart_set_mode(&go->gtt, CHART_FILL);
738 	chart_set_range(&go->gtt, 0, go->gem_objects.max_gtt);
739 }
740 
show_gem_objects(struct overlay_context * ctx,struct overlay_gem_objects * go)741 static void show_gem_objects(struct overlay_context *ctx, struct overlay_gem_objects *go)
742 {
743 	struct gem_objects_comm *comm;
744 	char buf[310];
745 	cairo_pattern_t *linear;
746 	int x, y, y1, y2;
747 
748 	if (go->error == 0)
749 		go->error = gem_objects_update(&go->gem_objects);
750 	if (go->error)
751 		return;
752 
753 	cairo_rectangle(ctx->cr, ctx->width/2+HALF_PAD-.5, ctx->height/2+HALF_PAD-.5, ctx->width/2-SIZE_PAD+1, ctx->height/2-SIZE_PAD+1);
754 	cairo_set_source_rgb(ctx->cr, .15, .15, .15);
755 	cairo_set_line_width(ctx->cr, 1);
756 	cairo_stroke(ctx->cr);
757 
758 	chart_add_sample(&go->gtt, go->gem_objects.total_gtt);
759 	chart_add_sample(&go->aperture, go->gem_objects.total_aperture);
760 
761 	chart_draw(&go->gtt, ctx->cr);
762 	chart_draw(&go->aperture, ctx->cr);
763 
764 
765 	y = ctx->height/2 + HALF_PAD + 12 - 2;
766 	x = ctx->width/2 + HALF_PAD;
767 
768 	y2 = y1 = y;
769 	y2 += 14;
770 	for (comm = go->gem_objects.comm; comm; comm = comm->next) {
771 		if ((comm->bytes >> 20) == 0)
772 			break;
773 		y2 += 12;
774 	}
775 	y1 += -12 - 2;
776 	y2 += -12 + 4;
777 
778 	cairo_rectangle(ctx->cr, x, y1, ctx->width/2-SIZE_PAD, y2-y1);
779 	linear = cairo_pattern_create_linear(x, 0, x+ctx->width/2-SIZE_PAD, 0);
780 	cairo_pattern_add_color_stop_rgba(linear, 0, 0, 0, 0, .5);
781 	cairo_pattern_add_color_stop_rgba(linear, 1, 0, 0, 0, .0);
782 	cairo_set_source(ctx->cr, linear);
783 	cairo_pattern_destroy(linear);
784 	cairo_fill(ctx->cr);
785 
786 	sprintf(buf, "Total: %ldMB, %ld objects",
787 		go->gem_objects.total_bytes >> 20, go->gem_objects.total_count);
788 	cairo_set_source_rgba(ctx->cr, 1, 1, 1, 1);
789 	cairo_move_to(ctx->cr, x, y);
790 	cairo_show_text(ctx->cr, buf);
791 	y += 12;
792 
793 	cairo_set_source_rgba(ctx->cr, .8, .8, .8, 1);
794 	cairo_set_font_size(ctx->cr, 8);
795 	for (comm = go->gem_objects.comm; comm; comm = comm->next) {
796 		if ((comm->bytes >> 20) == 0)
797 			break;
798 
799 		sprintf(buf, "%s %ldMB, %ld objects",
800 			comm->name, comm->bytes >> 20, comm->count);
801 		cairo_move_to(ctx->cr, x, y);
802 		cairo_show_text(ctx->cr, buf);
803 		y += 12;
804 	}
805 }
806 
807 static int take_snapshot;
808 
signal_snapshot(int sig)809 static void signal_snapshot(int sig)
810 {
811 	take_snapshot = sig;
812 }
813 
get_sample_period(struct config * config)814 static int get_sample_period(struct config *config)
815 {
816 	const char *value;
817 
818 	value = config_get_value(config, "sampling", "period");
819 	if (value && atoi(value) > 0)
820 		return atoi(value);
821 
822 	value = config_get_value(config, "sampling", "frequency");
823 	if (value && atoi(value) > 0)
824 		return 1000000 / atoi(value);
825 
826 	return 500000;
827 }
828 
overlay_snapshot(struct overlay_context * ctx)829 static void overlay_snapshot(struct overlay_context *ctx)
830 {
831 	char buf[1024];
832 	sprintf(buf, "/tmp/overlay-snapshot-%ld.png", (long)time(NULL));
833 	cairo_surface_write_to_png(ctx->surface, buf);
834 }
835 
usage(const char * progname)836 static void usage(const char *progname)
837 {
838 	printf("intel-gpu-overlay -- realtime display of GPU statistics\n");
839 	printf("Usage: %s [options]\n", progname);
840 	printf("\t--config|-c <string> | <filename>\t\t\tSpecify an ini-style configuration string or file\n");
841 	printf("\t--geometry|-G <width>x<height>+<x-offset>+<y-offset>\tExact window placement and size\n");
842 	printf("\t--position|-P (top|middle|bottom)-(left|centre|right)\tPlace the window in a particular corner\n");
843 	printf("\t--size|-S <width>x<height> | <scale>%%\t\t\tWindow size\n");
844 	printf("\t--foreground|-f\t\t\t\t\t\tKeep the application in foreground\n");
845 	printf("\t--help|-h\t\t\t\t\t\tThis help message\n");
846 }
847 
main(int argc,char ** argv)848 int main(int argc, char **argv)
849 {
850 	static struct option long_options[] = {
851 		{"config", 1, 0, 'c'},
852 		{"geometry", 1, 0, 'G'},
853 		{"position", 1, 0, 'P'},
854 		{"size", 1, 0, 'S'},
855 		{"foreground", 0, 0, 'f'},
856 		{"help", 0, 0, 'h'},
857 		{NULL, 0, 0, 0,}
858 	};
859 	struct overlay_context ctx;
860 	struct config config;
861 	int index, sample_period;
862 	int daemonize = 1, renice = 0;
863 	int i;
864 
865 	setlocale(LC_ALL, "");
866 	config_init(&config);
867 
868 	opterr = 0;
869 	while ((i = getopt_long(argc, argv, "c:G:fhn?", long_options, &index)) != -1) {
870 		switch (i) {
871 		case 'c':
872 			config_parse_string(&config, optarg);
873 			break;
874 		case 'G':
875 			config_set_value(&config, "window", "geometry", optarg);
876 			break;
877 		case 'P':
878 			config_set_value(&config, "window", "position", optarg);
879 			break;
880 		case 'S':
881 			config_set_value(&config, "window", "size", optarg);
882 			break;
883 		case 'f':
884 			daemonize = 0;
885 			break;
886 		case 'n':
887 			renice = -20;
888 			if (optarg)
889 				renice = atoi(optarg);
890 			break;
891 		case 'h':
892 			usage(argv[0]);
893 			return 0;
894 		}
895 	}
896 
897 	if (argc > optind) {
898 		x11_overlay_stop();
899 		return 0;
900 	}
901 
902 	ctx.width = 640;
903 	ctx.height = 236;
904 	ctx.surface = NULL;
905 	if (ctx.surface == NULL)
906 		ctx.surface = x11_overlay_create(&config, &ctx.width, &ctx.height);
907 	if (ctx.surface == NULL)
908 		ctx.surface = x11_window_create(&config, &ctx.width, &ctx.height);
909 	if (ctx.surface == NULL)
910 		ctx.surface = kms_overlay_create(&config, &ctx.width, &ctx.height);
911 	if (ctx.surface == NULL)
912 		return ENXIO;
913 
914 	if (daemonize && daemon(0, 0))
915 		return EINVAL;
916 
917 	if (renice && (nice(renice) == -1))
918 		fprintf(stderr, "Could not renice: %s\n", strerror(errno));
919 
920 	signal(SIGUSR1, signal_snapshot);
921 
922 	debugfs_init();
923 
924 	init_gpu_top(&ctx, &ctx.gpu_top);
925 	init_gpu_perf(&ctx, &ctx.gpu_perf);
926 	init_gpu_freq(&ctx, &ctx.gpu_freq);
927 	init_gem_objects(&ctx, &ctx.gem_objects);
928 
929 	sample_period = get_sample_period(&config);
930 
931 	i = 0;
932 	while (1) {
933 		ctx.time = time(NULL);
934 
935 		ctx.cr = cairo_create(ctx.surface);
936 		cairo_set_operator(ctx.cr, CAIRO_OPERATOR_CLEAR);
937 		cairo_paint(ctx.cr);
938 		cairo_set_operator(ctx.cr, CAIRO_OPERATOR_OVER);
939 
940 		show_gpu_top(&ctx, &ctx.gpu_top);
941 		show_gpu_perf(&ctx, &ctx.gpu_perf);
942 		show_gpu_freq(&ctx, &ctx.gpu_freq);
943 		show_gem_objects(&ctx, &ctx.gem_objects);
944 
945 		{
946 			char buf[80];
947 			cairo_text_extents_t extents;
948 			gethostname(buf, sizeof(buf));
949 			cairo_set_source_rgb(ctx.cr, .5, .5, .5);
950 			cairo_set_font_size(ctx.cr, PAD-2);
951 			cairo_text_extents(ctx.cr, buf, &extents);
952 			cairo_move_to(ctx.cr,
953 				      (ctx.width-extents.width)/2.,
954 				      1+extents.height);
955 			cairo_show_text(ctx.cr, buf);
956 		}
957 
958 		cairo_destroy(ctx.cr);
959 
960 		overlay_show(ctx.surface);
961 
962 		if (take_snapshot) {
963 			overlay_snapshot(&ctx);
964 			take_snapshot = 0;
965 		}
966 
967 		usleep(sample_period);
968 	}
969 
970 	return 0;
971 }
972