]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
perf annotate-data: Implement folding in TUI browser
authorNamhyung Kim <namhyung@kernel.org>
Mon, 12 Aug 2024 19:44:46 +0000 (12:44 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 12 Aug 2024 21:04:35 +0000 (18:04 -0300)
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>
tools/perf/ui/browsers/annotate-data.c

index 04c73b67cd6c78e144a350180ab293251e57a20a..a5c5ad63425e5a5a8364daa820ca1faa66c34a35 100644 (file)
 #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 '>':