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 <mark@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/37802>
This commit is contained in:
Dhruv Mark Collins 2026-03-09 23:19:49 +00:00
parent da089bf741
commit 152b9c8db3

View file

@ -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");
}
}
}
}