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