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