perf annotate-data: Implement folding in TUI browser

Like 'perf report', use 'e' or 'E' key to toggle folding the current
entry so that it can control displaying child entries.

Note I didn't add the 'c' and 'C' key to collapse the entry because it's
also handled with the 'e'/'E' since it toggles the state.

Committer testing:

Do some 'perf mem record' for some workload of the whole system, using
the target options, as usual (--pid/-p, -C/--cpu, -a for the system wide
profiling, etc) and then:

  # perf annotate --skip-empty --data-type=pthread_mutex_t

That, by default, will start as --tui, then press 'E' to see the whole
struct unfolded, etc.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240812194447.2049187-3-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Namhyung Kim 2024-08-12 12:44:46 -07:00 committed by Arnaldo Carvalho de Melo
parent 05fc5b7de3
commit af73856e9a

View File

@ -18,12 +18,6 @@
#define UNFOLD_SIGN '-'
#define NOCHLD_SIGN ' '
struct annotated_data_browser {
struct ui_browser b;
struct list_head entries;
int nr_events;
};
struct browser_entry {
struct list_head node;
struct annotated_member *data;
@ -35,6 +29,13 @@ struct browser_entry {
bool folded; /* only can be false when it has children */
};
struct annotated_data_browser {
struct ui_browser b;
struct list_head entries;
struct browser_entry *curr;
int nr_events;
};
static struct annotated_data_browser *get_browser(struct ui_browser *uib)
{
return container_of(uib, struct annotated_data_browser, b);
@ -302,6 +303,7 @@ static void browser__seek(struct ui_browser *uib, off_t offset, int whence)
static unsigned int browser__refresh(struct ui_browser *uib)
{
struct annotated_data_browser *browser = get_browser(uib);
struct browser_entry *entry, *next;
int row = 0;
@ -314,6 +316,8 @@ static unsigned int browser__refresh(struct ui_browser *uib)
if (!uib->filter || !uib->filter(uib, &entry->node)) {
ui_browser__gotorc(uib, row, 0);
uib->write(uib, entry, row);
if (uib->top_idx + row == uib->index)
browser->curr = entry;
if (++row == uib->rows)
break;
}
@ -438,6 +442,78 @@ static void browser__write(struct ui_browser *uib, void *entry, int row)
ui_browser__write_nstring(uib, "", uib->width);
}
static void annotated_data_browser__fold(struct annotated_data_browser *browser,
struct browser_entry *entry,
bool recursive)
{
struct browser_entry *child;
if (list_empty(&entry->children))
return;
if (entry->folded && !recursive)
return;
if (recursive) {
list_for_each_entry(child, &entry->children, node)
annotated_data_browser__fold(browser, child, true);
}
entry->nr_entries = 1;
entry->folded = true;
}
static void annotated_data_browser__unfold(struct annotated_data_browser *browser,
struct browser_entry *entry,
bool recursive)
{
struct browser_entry *child;
int nr_entries;
if (list_empty(&entry->children))
return;
if (!entry->folded && !recursive)
return;
nr_entries = 1; /* for self */
list_for_each_entry(child, &entry->children, node) {
if (recursive)
annotated_data_browser__unfold(browser, child, true);
nr_entries += child->nr_entries;
}
entry->nr_entries = nr_entries;
entry->folded = false;
}
static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser,
bool recursive)
{
struct browser_entry *curr = browser->curr;
struct browser_entry *parent;
parent = curr->parent;
while (parent) {
parent->nr_entries -= curr->nr_entries;
parent = parent->parent;
}
browser->b.nr_entries -= curr->nr_entries;
if (curr->folded)
annotated_data_browser__unfold(browser, curr, recursive);
else
annotated_data_browser__fold(browser, curr, recursive);
parent = curr->parent;
while (parent) {
parent->nr_entries += curr->nr_entries;
parent = parent->parent;
}
browser->b.nr_entries += curr->nr_entries;
assert(browser->b.nr_entries == count_visible_entries(browser));
}
static int annotated_data_browser__run(struct annotated_data_browser *browser,
struct evsel *evsel __maybe_unused,
struct hist_browser_timer *hbt)
@ -462,8 +538,18 @@ static int annotated_data_browser__run(struct annotated_data_browser *browser,
"UP/DOWN/PGUP\n"
"PGDN/SPACE Navigate\n"
"</> Move to prev/next symbol\n"
"e Expand/Collapse current entry\n"
"E Expand/Collapse all children of the current\n"
"q/ESC/CTRL+C Exit\n\n");
continue;
case 'e':
annotated_data_browser__toggle_fold(browser,
/*recursive=*/false);
break;
case 'E':
annotated_data_browser__toggle_fold(browser,
/*recursive=*/true);
break;
case K_LEFT:
case '<':
case '>':