~mmach/netext73/mesa-haswell

« back to all changes in this revision

Viewing changes to src/freedreno/perfcntrs/fdperf.c

  • Committer: mmach
  • Date: 2022-09-22 19:56:13 UTC
  • Revision ID: netbit73@gmail.com-20220922195613-wtik9mmy20tmor0i
2022-09-22 21:17:09

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2016 Rob Clark <robclark@freedesktop.org>
3
 
 * All Rights Reserved.
4
 
 *
5
 
 * Permission is hereby granted, free of charge, to any person obtaining a
6
 
 * copy of this software and associated documentation files (the "Software"),
7
 
 * to deal in the Software without restriction, including without limitation
8
 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
 
 * and/or sell copies of the Software, and to permit persons to whom the
10
 
 * Software is furnished to do so, subject to the following conditions:
11
 
 *
12
 
 * The above copyright notice and this permission notice (including the next
13
 
 * paragraph) shall be included in all copies or substantial portions of the
14
 
 * Software.
15
 
 *
16
 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19
 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
 
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
 
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
 
 * OTHER DEALINGS IN THE SOFTWARE.
23
 
 */
24
 
 
25
 
#include <assert.h>
26
 
#include <curses.h>
27
 
#include <err.h>
28
 
#include <inttypes.h>
29
 
#include <libconfig.h>
30
 
#include <locale.h>
31
 
#include <stdint.h>
32
 
#include <stdio.h>
33
 
#include <stdlib.h>
34
 
#include <string.h>
35
 
#include <time.h>
36
 
#include <xf86drm.h>
37
 
 
38
 
#include "drm/freedreno_drmif.h"
39
 
#include "drm/freedreno_ringbuffer.h"
40
 
 
41
 
#include "util/os_file.h"
42
 
 
43
 
#include "freedreno_dt.h"
44
 
#include "freedreno_perfcntr.h"
45
 
 
46
 
#define MAX_CNTR_PER_GROUP 24
47
 
 
48
 
/* NOTE first counter group should always be CP, since we unconditionally
49
 
 * use CP counter to measure the gpu freq.
50
 
 */
51
 
 
52
 
struct counter_group {
53
 
   const struct fd_perfcntr_group *group;
54
 
 
55
 
   struct {
56
 
      const struct fd_perfcntr_counter *counter;
57
 
      uint16_t select_val;
58
 
      volatile uint32_t *val_hi;
59
 
      volatile uint32_t *val_lo;
60
 
   } counter[MAX_CNTR_PER_GROUP];
61
 
 
62
 
   /* last sample time: */
63
 
   uint32_t stime[MAX_CNTR_PER_GROUP];
64
 
   /* for now just care about the low 32b value.. at least then we don't
65
 
    * have to really care that we can't sample both hi and lo regs at the
66
 
    * same time:
67
 
    */
68
 
   uint32_t last[MAX_CNTR_PER_GROUP];
69
 
   /* current value, ie. by how many did the counter increase in last
70
 
    * sampling period divided by the sampling period:
71
 
    */
72
 
   float current[MAX_CNTR_PER_GROUP];
73
 
   /* name of currently selected counters (for UI): */
74
 
   const char *label[MAX_CNTR_PER_GROUP];
75
 
};
76
 
 
77
 
static struct {
78
 
   void *io;
79
 
   uint32_t chipid;
80
 
   uint32_t min_freq;
81
 
   uint32_t max_freq;
82
 
   /* per-generation table of counters: */
83
 
   unsigned ngroups;
84
 
   struct counter_group *groups;
85
 
   /* drm device (for writing select regs via ring): */
86
 
   struct fd_device *dev;
87
 
   struct fd_pipe *pipe;
88
 
   struct fd_submit *submit;
89
 
   struct fd_ringbuffer *ring;
90
 
} dev;
91
 
 
92
 
static void config_save(void);
93
 
static void config_restore(void);
94
 
static void restore_counter_groups(void);
95
 
 
96
 
/*
97
 
 * helpers
98
 
 */
99
 
 
100
 
static uint32_t
101
 
gettime_us(void)
102
 
{
103
 
   struct timespec ts;
104
 
   clock_gettime(CLOCK_MONOTONIC, &ts);
105
 
   return (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
106
 
}
107
 
 
108
 
static uint32_t
109
 
delta(uint32_t a, uint32_t b)
110
 
{
111
 
   /* deal with rollover: */
112
 
   if (a > b)
113
 
      return 0xffffffff - a + b;
114
 
   else
115
 
      return b - a;
116
 
}
117
 
 
118
 
static void
119
 
find_device(void)
120
 
{
121
 
   int ret;
122
 
 
123
 
   dev.dev = fd_device_open();
124
 
   if (!dev.dev)
125
 
      err(1, "could not open drm device");
126
 
 
127
 
   dev.pipe = fd_pipe_new(dev.dev, FD_PIPE_3D);
128
 
 
129
 
   uint64_t val;
130
 
   ret = fd_pipe_get_param(dev.pipe, FD_CHIP_ID, &val);
131
 
   if (ret) {
132
 
      err(1, "could not get gpu-id");
133
 
   }
134
 
   dev.chipid = val;
135
 
 
136
 
#define CHIP_FMT "d%d%d.%d"
137
 
#define CHIP_ARGS(chipid)                                                      \
138
 
   ((chipid) >> 24) & 0xff, ((chipid) >> 16) & 0xff, ((chipid) >> 8) & 0xff,   \
139
 
      ((chipid) >> 0) & 0xff
140
 
   printf("device: a%" CHIP_FMT "\n", CHIP_ARGS(dev.chipid));
141
 
 
142
 
   /* try MAX_FREQ first as that will work regardless of old dt
143
 
    * dt bindings vs upstream bindings:
144
 
    */
145
 
   ret = fd_pipe_get_param(dev.pipe, FD_MAX_FREQ, &val);
146
 
   if (ret) {
147
 
      printf("falling back to parsing DT bindings for freq\n");
148
 
      if (!fd_dt_find_freqs(&dev.min_freq, &dev.max_freq))
149
 
         err(1, "could not find GPU freqs");
150
 
   } else {
151
 
      dev.min_freq = 0;
152
 
      dev.max_freq = val;
153
 
   }
154
 
 
155
 
   printf("min_freq=%u, max_freq=%u\n", dev.min_freq, dev.max_freq);
156
 
 
157
 
   dev.io = fd_dt_find_io();
158
 
   if (!dev.io) {
159
 
      err(1, "could not map device");
160
 
   }
161
 
 
162
 
   fd_pipe_set_param(dev.pipe, FD_SYSPROF, 1);
163
 
}
164
 
 
165
 
/*
166
 
 * perf-monitor
167
 
 */
168
 
 
169
 
static void
170
 
flush_ring(void)
171
 
{
172
 
   int ret;
173
 
 
174
 
   if (!dev.submit)
175
 
      return;
176
 
 
177
 
   struct fd_submit_fence fence = {};
178
 
   util_queue_fence_init(&fence.ready);
179
 
 
180
 
   ret = fd_submit_flush(dev.submit, -1, &fence);
181
 
 
182
 
   if (ret)
183
 
      errx(1, "submit failed: %d", ret);
184
 
   util_queue_fence_wait(&fence.ready);
185
 
   fd_ringbuffer_del(dev.ring);
186
 
   fd_submit_del(dev.submit);
187
 
 
188
 
   dev.ring = NULL;
189
 
   dev.submit = NULL;
190
 
}
191
 
 
192
 
static void
193
 
select_counter(struct counter_group *group, int ctr, int n)
194
 
{
195
 
   assert(n < group->group->num_countables);
196
 
   assert(ctr < group->group->num_counters);
197
 
 
198
 
   group->label[ctr] = group->group->countables[n].name;
199
 
   group->counter[ctr].select_val = n;
200
 
 
201
 
   if (!dev.submit) {
202
 
      dev.submit = fd_submit_new(dev.pipe);
203
 
      dev.ring = fd_submit_new_ringbuffer(
204
 
         dev.submit, 0x1000, FD_RINGBUFFER_PRIMARY | FD_RINGBUFFER_GROWABLE);
205
 
   }
206
 
 
207
 
   /* bashing select register directly while gpu is active will end
208
 
    * in tears.. so we need to write it via the ring:
209
 
    *
210
 
    * TODO it would help startup time, if gpu is loaded, to batch
211
 
    * all the initial writes and do a single flush.. although that
212
 
    * makes things more complicated for capturing inital sample value
213
 
    */
214
 
   struct fd_ringbuffer *ring = dev.ring;
215
 
   switch (dev.chipid >> 24) {
216
 
   case 2:
217
 
   case 3:
218
 
   case 4:
219
 
      OUT_PKT3(ring, CP_WAIT_FOR_IDLE, 1);
220
 
      OUT_RING(ring, 0x00000000);
221
 
 
222
 
      if (group->group->counters[ctr].enable) {
223
 
         OUT_PKT0(ring, group->group->counters[ctr].enable, 1);
224
 
         OUT_RING(ring, 0);
225
 
      }
226
 
 
227
 
      if (group->group->counters[ctr].clear) {
228
 
         OUT_PKT0(ring, group->group->counters[ctr].clear, 1);
229
 
         OUT_RING(ring, 1);
230
 
 
231
 
         OUT_PKT0(ring, group->group->counters[ctr].clear, 1);
232
 
         OUT_RING(ring, 0);
233
 
      }
234
 
 
235
 
      OUT_PKT0(ring, group->group->counters[ctr].select_reg, 1);
236
 
      OUT_RING(ring, n);
237
 
 
238
 
      if (group->group->counters[ctr].enable) {
239
 
         OUT_PKT0(ring, group->group->counters[ctr].enable, 1);
240
 
         OUT_RING(ring, 1);
241
 
      }
242
 
 
243
 
      break;
244
 
   case 5:
245
 
   case 6:
246
 
      OUT_PKT7(ring, CP_WAIT_FOR_IDLE, 0);
247
 
 
248
 
      if (group->group->counters[ctr].enable) {
249
 
         OUT_PKT4(ring, group->group->counters[ctr].enable, 1);
250
 
         OUT_RING(ring, 0);
251
 
      }
252
 
 
253
 
      if (group->group->counters[ctr].clear) {
254
 
         OUT_PKT4(ring, group->group->counters[ctr].clear, 1);
255
 
         OUT_RING(ring, 1);
256
 
 
257
 
         OUT_PKT4(ring, group->group->counters[ctr].clear, 1);
258
 
         OUT_RING(ring, 0);
259
 
      }
260
 
 
261
 
      OUT_PKT4(ring, group->group->counters[ctr].select_reg, 1);
262
 
      OUT_RING(ring, n);
263
 
 
264
 
      if (group->group->counters[ctr].enable) {
265
 
         OUT_PKT4(ring, group->group->counters[ctr].enable, 1);
266
 
         OUT_RING(ring, 1);
267
 
      }
268
 
 
269
 
      break;
270
 
   }
271
 
 
272
 
   group->last[ctr] = *group->counter[ctr].val_lo;
273
 
   group->stime[ctr] = gettime_us();
274
 
}
275
 
 
276
 
static void
277
 
resample_counter(struct counter_group *group, int ctr)
278
 
{
279
 
   uint32_t val = *group->counter[ctr].val_lo;
280
 
   uint32_t t = gettime_us();
281
 
   uint32_t dt = delta(group->stime[ctr], t);
282
 
   uint32_t dval = delta(group->last[ctr], val);
283
 
   group->current[ctr] = (float)dval * 1000000.0 / (float)dt;
284
 
   group->last[ctr] = val;
285
 
   group->stime[ctr] = t;
286
 
}
287
 
 
288
 
#define REFRESH_MS 500
289
 
 
290
 
/* sample all the counters: */
291
 
static void
292
 
resample(void)
293
 
{
294
 
   static uint64_t last_time;
295
 
   uint64_t current_time = gettime_us();
296
 
 
297
 
   if ((current_time - last_time) < (REFRESH_MS * 1000 / 2))
298
 
      return;
299
 
 
300
 
   last_time = current_time;
301
 
 
302
 
   for (unsigned i = 0; i < dev.ngroups; i++) {
303
 
      struct counter_group *group = &dev.groups[i];
304
 
      for (unsigned j = 0; j < group->group->num_counters; j++) {
305
 
         resample_counter(group, j);
306
 
      }
307
 
   }
308
 
}
309
 
 
310
 
/*
311
 
 * The UI
312
 
 */
313
 
 
314
 
#define COLOR_GROUP_HEADER 1
315
 
#define COLOR_FOOTER       2
316
 
#define COLOR_INVERSE      3
317
 
 
318
 
static int w, h;
319
 
static int ctr_width;
320
 
static int max_rows, current_cntr = 1;
321
 
 
322
 
static void
323
 
redraw_footer(WINDOW *win)
324
 
{
325
 
   char *footer;
326
 
   int n;
327
 
 
328
 
   n = asprintf(&footer, " fdperf: a%" CHIP_FMT " (%.2fMHz..%.2fMHz)",
329
 
                CHIP_ARGS(dev.chipid), ((float)dev.min_freq) / 1000000.0,
330
 
                ((float)dev.max_freq) / 1000000.0);
331
 
 
332
 
   wmove(win, h - 1, 0);
333
 
   wattron(win, COLOR_PAIR(COLOR_FOOTER));
334
 
   waddstr(win, footer);
335
 
   whline(win, ' ', w - n);
336
 
   wattroff(win, COLOR_PAIR(COLOR_FOOTER));
337
 
 
338
 
   free(footer);
339
 
}
340
 
 
341
 
static void
342
 
redraw_group_header(WINDOW *win, int row, const char *name)
343
 
{
344
 
   wmove(win, row, 0);
345
 
   wattron(win, A_BOLD);
346
 
   wattron(win, COLOR_PAIR(COLOR_GROUP_HEADER));
347
 
   waddstr(win, name);
348
 
   whline(win, ' ', w - strlen(name));
349
 
   wattroff(win, COLOR_PAIR(COLOR_GROUP_HEADER));
350
 
   wattroff(win, A_BOLD);
351
 
}
352
 
 
353
 
static void
354
 
redraw_counter_label(WINDOW *win, int row, const char *name, bool selected)
355
 
{
356
 
   int n = strlen(name);
357
 
   assert(n <= ctr_width);
358
 
   wmove(win, row, 0);
359
 
   whline(win, ' ', ctr_width - n);
360
 
   wmove(win, row, ctr_width - n);
361
 
   if (selected)
362
 
      wattron(win, COLOR_PAIR(COLOR_INVERSE));
363
 
   waddstr(win, name);
364
 
   if (selected)
365
 
      wattroff(win, COLOR_PAIR(COLOR_INVERSE));
366
 
   waddstr(win, ": ");
367
 
}
368
 
 
369
 
static void
370
 
redraw_counter_value_cycles(WINDOW *win, float val)
371
 
{
372
 
   char *str;
373
 
   int x = getcurx(win);
374
 
   int valwidth = w - x;
375
 
   int barwidth, n;
376
 
 
377
 
   /* convert to fraction of max freq: */
378
 
   val = val / (float)dev.max_freq;
379
 
 
380
 
   /* figure out percentage-bar width: */
381
 
   barwidth = (int)(val * valwidth);
382
 
 
383
 
   /* sometimes things go over 100%.. idk why, could be
384
 
    * things running faster than base clock, or counter
385
 
    * summing up cycles in multiple cores?
386
 
    */
387
 
   barwidth = MIN2(barwidth, valwidth - 1);
388
 
 
389
 
   n = asprintf(&str, "%.2f%%", 100.0 * val);
390
 
   wattron(win, COLOR_PAIR(COLOR_INVERSE));
391
 
   waddnstr(win, str, barwidth);
392
 
   if (barwidth > n) {
393
 
      whline(win, ' ', barwidth - n);
394
 
      wmove(win, getcury(win), x + barwidth);
395
 
   }
396
 
   wattroff(win, COLOR_PAIR(COLOR_INVERSE));
397
 
   if (barwidth < n)
398
 
      waddstr(win, str + barwidth);
399
 
   whline(win, ' ', w - getcurx(win));
400
 
 
401
 
   free(str);
402
 
}
403
 
 
404
 
static void
405
 
redraw_counter_value_raw(WINDOW *win, float val)
406
 
{
407
 
   char *str;
408
 
   (void)asprintf(&str, "%'.2f", val);
409
 
   waddstr(win, str);
410
 
   whline(win, ' ', w - getcurx(win));
411
 
   free(str);
412
 
}
413
 
 
414
 
static void
415
 
redraw_counter(WINDOW *win, int row, struct counter_group *group, int ctr,
416
 
               bool selected)
417
 
{
418
 
   redraw_counter_label(win, row, group->label[ctr], selected);
419
 
 
420
 
   /* quick hack, if the label has "CYCLE" in the name, it is
421
 
    * probably a cycle counter ;-)
422
 
    * Perhaps add more info in rnndb schema to know how to
423
 
    * treat individual counters (ie. which are cycles, and
424
 
    * for those we want to present as a percentage do we
425
 
    * need to scale the result.. ie. is it running at some
426
 
    * multiple or divisor of core clk, etc)
427
 
    *
428
 
    * TODO it would be much more clever to get this from xml
429
 
    * Also.. in some cases I think we want to know how many
430
 
    * units the counter is counting for, ie. if a320 has 2x
431
 
    * shader as a306 we might need to scale the result..
432
 
    */
433
 
   if (strstr(group->label[ctr], "CYCLE") ||
434
 
       strstr(group->label[ctr], "BUSY") || strstr(group->label[ctr], "IDLE"))
435
 
      redraw_counter_value_cycles(win, group->current[ctr]);
436
 
   else
437
 
      redraw_counter_value_raw(win, group->current[ctr]);
438
 
}
439
 
 
440
 
static void
441
 
redraw(WINDOW *win)
442
 
{
443
 
   static int scroll = 0;
444
 
   int max, row = 0;
445
 
 
446
 
   w = getmaxx(win);
447
 
   h = getmaxy(win);
448
 
 
449
 
   max = h - 3;
450
 
 
451
 
   if ((current_cntr - scroll) > (max - 1)) {
452
 
      scroll = current_cntr - (max - 1);
453
 
   } else if ((current_cntr - 1) < scroll) {
454
 
      scroll = current_cntr - 1;
455
 
   }
456
 
 
457
 
   for (unsigned i = 0; i < dev.ngroups; i++) {
458
 
      struct counter_group *group = &dev.groups[i];
459
 
      unsigned j = 0;
460
 
 
461
 
      /* NOTE skip CP the first CP counter */
462
 
      if (i == 0)
463
 
         j++;
464
 
 
465
 
      if (j < group->group->num_counters) {
466
 
         if ((scroll <= row) && ((row - scroll) < max))
467
 
            redraw_group_header(win, row - scroll, group->group->name);
468
 
         row++;
469
 
      }
470
 
 
471
 
      for (; j < group->group->num_counters; j++) {
472
 
         if ((scroll <= row) && ((row - scroll) < max))
473
 
            redraw_counter(win, row - scroll, group, j, row == current_cntr);
474
 
         row++;
475
 
      }
476
 
   }
477
 
 
478
 
   /* convert back to physical (unscrolled) offset: */
479
 
   row = max;
480
 
 
481
 
   redraw_group_header(win, row, "Status");
482
 
   row++;
483
 
 
484
 
   /* Draw GPU freq row: */
485
 
   redraw_counter_label(win, row, "Freq (MHz)", false);
486
 
   redraw_counter_value_raw(win, dev.groups[0].current[0] / 1000000.0);
487
 
   row++;
488
 
 
489
 
   redraw_footer(win);
490
 
 
491
 
   refresh();
492
 
}
493
 
 
494
 
static struct counter_group *
495
 
current_counter(int *ctr)
496
 
{
497
 
   int n = 0;
498
 
 
499
 
   for (unsigned i = 0; i < dev.ngroups; i++) {
500
 
      struct counter_group *group = &dev.groups[i];
501
 
      unsigned j = 0;
502
 
 
503
 
      /* NOTE skip the first CP counter (CP_ALWAYS_COUNT) */
504
 
      if (i == 0)
505
 
         j++;
506
 
 
507
 
      /* account for group header: */
508
 
      if (j < group->group->num_counters) {
509
 
         /* cannot select group header.. return null to indicate this
510
 
          * main_ui():
511
 
          */
512
 
         if (n == current_cntr)
513
 
            return NULL;
514
 
         n++;
515
 
      }
516
 
 
517
 
      for (; j < group->group->num_counters; j++) {
518
 
         if (n == current_cntr) {
519
 
            if (ctr)
520
 
               *ctr = j;
521
 
            return group;
522
 
         }
523
 
         n++;
524
 
      }
525
 
   }
526
 
 
527
 
   assert(0);
528
 
   return NULL;
529
 
}
530
 
 
531
 
static void
532
 
counter_dialog(void)
533
 
{
534
 
   WINDOW *dialog;
535
 
   struct counter_group *group;
536
 
   int cnt = 0, current = 0, scroll;
537
 
 
538
 
   /* figure out dialog size: */
539
 
   int dh = h / 2;
540
 
   int dw = ctr_width + 2;
541
 
 
542
 
   group = current_counter(&cnt);
543
 
 
544
 
   /* find currently selected idx (note there can be discontinuities
545
 
    * so the selected value does not map 1:1 to current idx)
546
 
    */
547
 
   uint32_t selected = group->counter[cnt].select_val;
548
 
   for (int i = 0; i < group->group->num_countables; i++) {
549
 
      if (group->group->countables[i].selector == selected) {
550
 
         current = i;
551
 
         break;
552
 
      }
553
 
   }
554
 
 
555
 
   /* scrolling offset, if dialog is too small for all the choices: */
556
 
   scroll = 0;
557
 
 
558
 
   dialog = newwin(dh, dw, (h - dh) / 2, (w - dw) / 2);
559
 
   box(dialog, 0, 0);
560
 
   wrefresh(dialog);
561
 
   keypad(dialog, TRUE);
562
 
 
563
 
   while (true) {
564
 
      int max = MIN2(dh - 2, group->group->num_countables);
565
 
      int selector = -1;
566
 
 
567
 
      if ((current - scroll) >= (dh - 3)) {
568
 
         scroll = current - (dh - 3);
569
 
      } else if (current < scroll) {
570
 
         scroll = current;
571
 
      }
572
 
 
573
 
      for (int i = 0; i < max; i++) {
574
 
         int n = scroll + i;
575
 
         wmove(dialog, i + 1, 1);
576
 
         if (n == current) {
577
 
            assert(n < group->group->num_countables);
578
 
            selector = group->group->countables[n].selector;
579
 
            wattron(dialog, COLOR_PAIR(COLOR_INVERSE));
580
 
         }
581
 
         if (n < group->group->num_countables)
582
 
            waddstr(dialog, group->group->countables[n].name);
583
 
         whline(dialog, ' ', dw - getcurx(dialog) - 1);
584
 
         if (n == current)
585
 
            wattroff(dialog, COLOR_PAIR(COLOR_INVERSE));
586
 
      }
587
 
 
588
 
      assert(selector >= 0);
589
 
 
590
 
      switch (wgetch(dialog)) {
591
 
      case KEY_UP:
592
 
         current = MAX2(0, current - 1);
593
 
         break;
594
 
      case KEY_DOWN:
595
 
         current = MIN2(group->group->num_countables - 1, current + 1);
596
 
         break;
597
 
      case KEY_LEFT:
598
 
      case KEY_ENTER:
599
 
         /* select new sampler */
600
 
         select_counter(group, cnt, selector);
601
 
         flush_ring();
602
 
         config_save();
603
 
         goto out;
604
 
      case 'q':
605
 
         goto out;
606
 
      default:
607
 
         /* ignore */
608
 
         break;
609
 
      }
610
 
 
611
 
      resample();
612
 
   }
613
 
 
614
 
out:
615
 
   wborder(dialog, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
616
 
   delwin(dialog);
617
 
}
618
 
 
619
 
static void
620
 
scroll_cntr(int amount)
621
 
{
622
 
   if (amount < 0) {
623
 
      current_cntr = MAX2(1, current_cntr + amount);
624
 
      if (current_counter(NULL) == NULL) {
625
 
         current_cntr = MAX2(1, current_cntr - 1);
626
 
      }
627
 
   } else {
628
 
      current_cntr = MIN2(max_rows - 1, current_cntr + amount);
629
 
      if (current_counter(NULL) == NULL)
630
 
         current_cntr = MIN2(max_rows - 1, current_cntr + 1);
631
 
   }
632
 
}
633
 
 
634
 
static void
635
 
main_ui(void)
636
 
{
637
 
   WINDOW *mainwin;
638
 
   uint32_t last_time = gettime_us();
639
 
 
640
 
   /* curses setup: */
641
 
   mainwin = initscr();
642
 
   if (!mainwin)
643
 
      goto out;
644
 
 
645
 
   cbreak();
646
 
   wtimeout(mainwin, REFRESH_MS);
647
 
   noecho();
648
 
   keypad(mainwin, TRUE);
649
 
   curs_set(0);
650
 
   start_color();
651
 
   init_pair(COLOR_GROUP_HEADER, COLOR_WHITE, COLOR_GREEN);
652
 
   init_pair(COLOR_FOOTER, COLOR_WHITE, COLOR_BLUE);
653
 
   init_pair(COLOR_INVERSE, COLOR_BLACK, COLOR_WHITE);
654
 
 
655
 
   while (true) {
656
 
      switch (wgetch(mainwin)) {
657
 
      case KEY_UP:
658
 
         scroll_cntr(-1);
659
 
         break;
660
 
      case KEY_DOWN:
661
 
         scroll_cntr(+1);
662
 
         break;
663
 
      case KEY_NPAGE: /* page-down */
664
 
         /* TODO figure out # of rows visible? */
665
 
         scroll_cntr(+15);
666
 
         break;
667
 
      case KEY_PPAGE: /* page-up */
668
 
         /* TODO figure out # of rows visible? */
669
 
         scroll_cntr(-15);
670
 
         break;
671
 
      case KEY_RIGHT:
672
 
         counter_dialog();
673
 
         break;
674
 
      case 'q':
675
 
         goto out;
676
 
         break;
677
 
      default:
678
 
         /* ignore */
679
 
         break;
680
 
      }
681
 
      resample();
682
 
      redraw(mainwin);
683
 
 
684
 
      /* restore the counters every 0.5s in case the GPU has suspended,
685
 
       * in which case the current selected countables will have reset:
686
 
       */
687
 
      uint32_t t = gettime_us();
688
 
      if (delta(last_time, t) > 500000) {
689
 
         restore_counter_groups();
690
 
         flush_ring();
691
 
         last_time = t;
692
 
      }
693
 
   }
694
 
 
695
 
   /* restore settings.. maybe we need an atexit()??*/
696
 
out:
697
 
   delwin(mainwin);
698
 
   endwin();
699
 
   refresh();
700
 
}
701
 
 
702
 
static void
703
 
restore_counter_groups(void)
704
 
{
705
 
   for (unsigned i = 0; i < dev.ngroups; i++) {
706
 
      struct counter_group *group = &dev.groups[i];
707
 
      unsigned j = 0;
708
 
 
709
 
      /* NOTE skip CP the first CP counter */
710
 
      if (i == 0)
711
 
         j++;
712
 
 
713
 
      for (; j < group->group->num_counters; j++) {
714
 
         select_counter(group, j, group->counter[j].select_val);
715
 
      }
716
 
   }
717
 
}
718
 
 
719
 
static void
720
 
setup_counter_groups(const struct fd_perfcntr_group *groups)
721
 
{
722
 
   for (unsigned i = 0; i < dev.ngroups; i++) {
723
 
      struct counter_group *group = &dev.groups[i];
724
 
 
725
 
      group->group = &groups[i];
726
 
 
727
 
      max_rows += group->group->num_counters + 1;
728
 
 
729
 
      /* the first CP counter is hidden: */
730
 
      if (i == 0) {
731
 
         max_rows--;
732
 
         if (group->group->num_counters <= 1)
733
 
            max_rows--;
734
 
      }
735
 
 
736
 
      for (unsigned j = 0; j < group->group->num_counters; j++) {
737
 
         group->counter[j].counter = &group->group->counters[j];
738
 
 
739
 
         group->counter[j].val_hi =
740
 
            dev.io + (group->counter[j].counter->counter_reg_hi * 4);
741
 
         group->counter[j].val_lo =
742
 
            dev.io + (group->counter[j].counter->counter_reg_lo * 4);
743
 
 
744
 
         group->counter[j].select_val = j;
745
 
      }
746
 
 
747
 
      for (unsigned j = 0; j < group->group->num_countables; j++) {
748
 
         ctr_width =
749
 
            MAX2(ctr_width, strlen(group->group->countables[j].name) + 1);
750
 
      }
751
 
   }
752
 
}
753
 
 
754
 
/*
755
 
 * configuration / persistence
756
 
 */
757
 
 
758
 
static config_t cfg;
759
 
static config_setting_t *setting;
760
 
 
761
 
static void
762
 
config_save(void)
763
 
{
764
 
   for (unsigned i = 0; i < dev.ngroups; i++) {
765
 
      struct counter_group *group = &dev.groups[i];
766
 
      unsigned j = 0;
767
 
 
768
 
      /* NOTE skip CP the first CP counter */
769
 
      if (i == 0)
770
 
         j++;
771
 
 
772
 
      config_setting_t *sect =
773
 
         config_setting_get_member(setting, group->group->name);
774
 
 
775
 
      for (; j < group->group->num_counters; j++) {
776
 
         char name[] = "counter0000";
777
 
         sprintf(name, "counter%d", j);
778
 
         config_setting_t *s = config_setting_lookup(sect, name);
779
 
         config_setting_set_int(s, group->counter[j].select_val);
780
 
      }
781
 
   }
782
 
 
783
 
   config_write_file(&cfg, "fdperf.cfg");
784
 
}
785
 
 
786
 
static void
787
 
config_restore(void)
788
 
{
789
 
   char *str;
790
 
 
791
 
   config_init(&cfg);
792
 
 
793
 
   /* Read the file. If there is an error, report it and exit. */
794
 
   if (!config_read_file(&cfg, "fdperf.cfg")) {
795
 
      warn("could not restore settings");
796
 
   }
797
 
 
798
 
   config_setting_t *root = config_root_setting(&cfg);
799
 
 
800
 
   /* per device settings: */
801
 
   (void)asprintf(&str, "a%dxx", dev.chipid >> 24);
802
 
   setting = config_setting_get_member(root, str);
803
 
   if (!setting)
804
 
      setting = config_setting_add(root, str, CONFIG_TYPE_GROUP);
805
 
   free(str);
806
 
 
807
 
   for (unsigned i = 0; i < dev.ngroups; i++) {
808
 
      struct counter_group *group = &dev.groups[i];
809
 
      unsigned j = 0;
810
 
 
811
 
      /* NOTE skip CP the first CP counter */
812
 
      if (i == 0)
813
 
         j++;
814
 
 
815
 
      config_setting_t *sect =
816
 
         config_setting_get_member(setting, group->group->name);
817
 
 
818
 
      if (!sect) {
819
 
         sect =
820
 
            config_setting_add(setting, group->group->name, CONFIG_TYPE_GROUP);
821
 
      }
822
 
 
823
 
      for (; j < group->group->num_counters; j++) {
824
 
         char name[] = "counter0000";
825
 
         sprintf(name, "counter%d", j);
826
 
         config_setting_t *s = config_setting_lookup(sect, name);
827
 
         if (!s) {
828
 
            config_setting_add(sect, name, CONFIG_TYPE_INT);
829
 
            continue;
830
 
         }
831
 
         select_counter(group, j, config_setting_get_int(s));
832
 
      }
833
 
   }
834
 
}
835
 
 
836
 
/*
837
 
 * main
838
 
 */
839
 
 
840
 
int
841
 
main(int argc, char **argv)
842
 
{
843
 
   find_device();
844
 
 
845
 
   const struct fd_perfcntr_group *groups;
846
 
   struct fd_dev_id dev_id = {
847
 
         .gpu_id = (dev.chipid >> 24) * 100,
848
 
   };
849
 
   groups = fd_perfcntrs(&dev_id, &dev.ngroups);
850
 
   if (!groups) {
851
 
      errx(1, "no perfcntr support");
852
 
   }
853
 
 
854
 
   dev.groups = calloc(dev.ngroups, sizeof(struct counter_group));
855
 
 
856
 
   setlocale(LC_NUMERIC, "en_US.UTF-8");
857
 
 
858
 
   setup_counter_groups(groups);
859
 
   restore_counter_groups();
860
 
   config_restore();
861
 
   flush_ring();
862
 
 
863
 
   main_ui();
864
 
 
865
 
   return 0;
866
 
}