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(>->cpu_top);
156 gpu_top_init(>->gpu_top);
157
158 chart_init(>->cpu, "CPU", 120);
159 chart_set_position(>->cpu, PAD, PAD);
160 chart_set_size(>->cpu, ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
161 chart_set_stroke_rgba(>->cpu, 0.75, 0.25, 0.75, 1.);
162 chart_set_mode(>->cpu, CHART_STROKE);
163 chart_set_range(>->cpu, 0, 100);
164
165 for (n = 0; n < gt->gpu_top.num_rings; n++) {
166 chart_init(>->busy[n],
167 gt->gpu_top.ring[n].name,
168 120);
169 chart_set_position(>->busy[n], PAD, PAD);
170 chart_set_size(>->busy[n], ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
171 chart_set_stroke_rgba(>->busy[n],
172 rgba[n][0], rgba[n][1], rgba[n][2], rgba[n][3]);
173 chart_set_mode(>->busy[n], CHART_STROKE);
174 chart_set_range(>->busy[n], 0, 100);
175 }
176
177 for (n = 0; n < gt->gpu_top.num_rings; n++) {
178 chart_init(>->wait[n],
179 gt->gpu_top.ring[n].name,
180 120);
181 chart_set_position(>->wait[n], PAD, PAD);
182 chart_set_size(>->wait[n], ctx->width/2 - SIZE_PAD, ctx->height/2 - SIZE_PAD);
183 chart_set_fill_rgba(>->wait[n],
184 rgba[n][0], rgba[n][1], rgba[n][2], rgba[n][3] * 0.70);
185 chart_set_mode(>->wait[n], CHART_FILL);
186 chart_set_range(>->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(>->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(>->cpu_top) == 0)
206 chart_add_sample(>->cpu, gt->cpu_top.busy);
207
208 for (n = 0; n < gt->gpu_top.num_rings; n++) {
209 if (update)
210 chart_add_sample(>->wait[n],
211 gt->gpu_top.ring[n].u.u.wait + gt->gpu_top.ring[n].u.u.sema);
212 chart_draw(>->wait[n], ctx->cr);
213 }
214 for (n = 0; n < gt->gpu_top.num_rings; n++) {
215 if (update)
216 chart_add_sample(>->busy[n],
217 gt->gpu_top.ring[n].u.u.busy);
218 chart_draw(>->busy[n], ctx->cr);
219 }
220 chart_draw(>->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 =>->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