From 152b9c8db3ee168f3d7cb1d592ef8985e6c2ccc5 Mon Sep 17 00:00:00 2001 From: Dhruv Mark Collins Date: Mon, 9 Mar 2026 23:19:49 +0000 Subject: [PATCH] freedreno/fdperf: Detect when counter values are invalid The usage of two CP counters by latency sensitive autotuner will affect the operation of fdperf, this detects when counters have selectors that have been changed and marks them as invalid with corresponding UI cues. This also seems to detect selector values being dropped while the GPU is in sleep states and tends to be useful to catch that too. Signed-off-by: Dhruv Mark Collins Part-of: --- src/freedreno/perfcntrs/fdperf.c | 72 ++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/src/freedreno/perfcntrs/fdperf.c b/src/freedreno/perfcntrs/fdperf.c index 785a061a79d..c0f34ed0385 100644 --- a/src/freedreno/perfcntrs/fdperf.c +++ b/src/freedreno/perfcntrs/fdperf.c @@ -49,6 +49,7 @@ struct counter_group { const struct fd_perfcntr_counter *counter; uint16_t select_val; bool is_gpufreq_counter; + bool is_invalid; /* If the selector got overwritten by another value. */ } counter[MAX_CNTR_PER_GROUP]; /* name of currently selected counters (for UI): */ @@ -270,6 +271,12 @@ select_counter(struct counter_group *group, int ctr, int countable_val) } } +static uint32_t load_counter_selector(struct counter_group *group, int ctr) +{ + const struct fd_perfcntr_counter *counter = group->counter[ctr].counter; + return *((uint32_t *) (dev.io + counter->select_reg * 4)); +} + static uint64_t load_counter_value(struct counter_group *group, int ctr) { /* We can read the counter register value as an uint64_t, as long as the @@ -294,6 +301,16 @@ resample_counter(struct counter_group *group, int ctr, uint64_t sample_time) group->sample_time_delta[ctr] = delta(previous_sample_time, sample_time); } +static void +check_counter_invalid(struct counter_group *group, int ctr) +{ + if (group->counter[ctr].is_gpufreq_counter) + return; + + uint32_t hw_selector = load_counter_selector(group, ctr); + group->counter[ctr].is_invalid = (hw_selector != group->counter[ctr].select_val); +} + /* sample all the counters: */ static void resample(void) @@ -310,6 +327,7 @@ resample(void) struct counter_group *group = &dev.groups[i]; for (unsigned j = 0; j < group->group->num_counters; j++) { resample_counter(group, j, current_time); + check_counter_invalid(group, j); } } } @@ -321,6 +339,7 @@ resample(void) #define COLOR_GROUP_HEADER 1 #define COLOR_FOOTER 2 #define COLOR_INVERSE 3 +#define COLOR_ERROR 4 static int w, h; static int ctr_width; @@ -354,7 +373,8 @@ redraw_group_header(WINDOW *win, int row, const char *name) } static void -redraw_counter_label(WINDOW *win, int row, const char *name, bool selected) +redraw_counter_label(WINDOW *win, int row, const char *name, bool selected, + bool is_invalid) { int n = strlen(name); assert(n <= ctr_width); @@ -363,9 +383,13 @@ redraw_counter_label(WINDOW *win, int row, const char *name, bool selected) wmove(win, row, ctr_width - n); if (selected) wattron(win, COLOR_PAIR(COLOR_INVERSE)); + else if (is_invalid) + wattron(win, COLOR_PAIR(COLOR_ERROR) | A_BOLD); waddstr(win, name); if (selected) wattroff(win, COLOR_PAIR(COLOR_INVERSE)); + else if (is_invalid) + wattroff(win, COLOR_PAIR(COLOR_ERROR) | A_BOLD); waddstr(win, ": "); } @@ -403,14 +427,19 @@ redraw_counter_value_cycles(WINDOW *win, float val) } static void -redraw_counter_value(WINDOW *win, int row, struct counter_group *group, int ctr) +redraw_counter_value(WINDOW *win, int row, struct counter_group *group, int ctr, + bool is_invalid) { char str[32]; int n = snprintf(str, sizeof(str), "%" PRIu64 " ", group->value_delta[ctr]); whline(win, ' ', 24 - n); wmove(win, row, getcurx(win) + 24 - n); + if (is_invalid) + wattron(win, COLOR_PAIR(COLOR_ERROR) | A_BOLD); waddstr(win, str); + if (is_invalid) + wattroff(win, COLOR_PAIR(COLOR_ERROR) | A_BOLD); /* quick hack, if the label has "CYCLE" in the name, it is * probably a cycle counter ;-) @@ -425,8 +454,9 @@ redraw_counter_value(WINDOW *win, int row, struct counter_group *group, int ctr) * units the counter is counting for, ie. if a320 has 2x * shader as a306 we might need to scale the result.. */ - if (strstr(group->label[ctr], "CYCLE") || - strstr(group->label[ctr], "BUSY") || strstr(group->label[ctr], "IDLE")) { + if (!is_invalid && (strstr(group->label[ctr], "CYCLE") || + strstr(group->label[ctr], "BUSY") || + strstr(group->label[ctr], "IDLE"))) { float cycles_val = (float) group->value_delta[ctr] * 1000000.0 / (float) group->sample_time_delta[ctr]; redraw_counter_value_cycles(win, cycles_val); @@ -439,14 +469,15 @@ static void redraw_counter(WINDOW *win, int row, struct counter_group *group, int ctr, bool selected) { - redraw_counter_label(win, row, group->label[ctr], selected); - redraw_counter_value(win, row, group, ctr); + bool is_invalid = group->counter[ctr].is_invalid; + redraw_counter_label(win, row, group->label[ctr], selected, is_invalid); + redraw_counter_value(win, row, group, ctr, is_invalid); } static void redraw_gpufreq_counter(WINDOW *win, int row) { - redraw_counter_label(win, row, "Freq (MHz)", false); + redraw_counter_label(win, row, "Freq (MHz)", false, false); struct counter_group *group = &dev.groups[0]; float freq_val = (float) group->value_delta[0] / (float) group->sample_time_delta[0]; @@ -672,6 +703,7 @@ main_ui(void) init_pair(COLOR_GROUP_HEADER, COLOR_WHITE, COLOR_GREEN); init_pair(COLOR_FOOTER, COLOR_WHITE, COLOR_BLUE); init_pair(COLOR_INVERSE, COLOR_BLACK, COLOR_WHITE); + init_pair(COLOR_ERROR, COLOR_RED, COLOR_BLACK); while (true) { switch (wgetch(mainwin)) { @@ -734,21 +766,27 @@ dump_counters(void) float val = (float) group->value_delta[j] * 1000000.0 / (float) group->sample_time_delta[j]; + bool is_invalid = group->counter[j].is_invalid; + int n = printf("%s: ", label) - 2; while (n++ < ctr_width) fputc(' ', stdout); - n = printf("%" PRIu64, group->value_delta[j]); - while (n++ < 24) - fputc(' ', stdout); - - if (strstr(label, "CYCLE") || - strstr(label, "BUSY") || - strstr(label, "IDLE")) { - val = val / dev.max_freq * 100.0f; - printf(" %.2f%%\n", val); + if (is_invalid) { + printf("[invalid]\n"); } else { - printf("\n"); + n = printf("%" PRIu64, group->value_delta[j]); + while (n++ < 24) + fputc(' ', stdout); + + if (strstr(label, "CYCLE") || + strstr(label, "BUSY") || + strstr(label, "IDLE")) { + val = val / dev.max_freq * 100.0f; + printf(" %.2f%%\n", val); + } else { + printf("\n"); + } } } }