Commit af73856e authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo
Browse files

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: default avatarNamhyung Kim <namhyung@kernel.org>
Tested-by: default avatarArnaldo 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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 05fc5b7d
Loading
Loading
Loading
Loading
+92 −6
Original line number Diff line number Diff line
@@ -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 '>':