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