Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf annotate-data: Add hist_entry__annotate_data_tui()

Support data type profiling output on TUI.

Testing from Arnaldo:

First make sure that the debug information for your workload binaries
in embedded in them by building it with '-g' or install the debuginfo
packages, since our workload is 'find':

root@number:~# type find
find is hashed (/usr/bin/find)
root@number:~# rpm -qf /usr/bin/find
findutils-4.9.0-5.fc39.x86_64
root@number:~# dnf debuginfo-install findutils
<SNIP>
root@number:~#

Then collect some data:

root@number:~# echo 1 > /proc/sys/vm/drop_caches
root@number:~# perf mem record find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.331 MB perf.data (3982 samples) ]
root@number:~#

Finally do data-type annotation with the following command, that will
default, as 'perf report' to the --tui mode, with lines colored to
highlight the hotspots, etc.

root@number:~# perf annotate --data-type
Annotate type: 'struct predicate' (58 samples)
Percent Offset Size Field
100.00 0 312 struct predicate {
0.00 0 8 PRED_FUNC pred_func;
0.00 8 8 char* p_name;
0.00 16 4 enum predicate_type p_type;
0.00 20 4 enum predicate_precedence p_prec;
0.00 24 1 _Bool side_effects;
0.00 25 1 _Bool no_default_print;
0.00 26 1 _Bool need_stat;
0.00 27 1 _Bool need_type;
0.00 28 1 _Bool need_inum;
0.00 32 4 enum EvaluationCost p_cost;
0.00 36 4 float est_success_rate;
0.00 40 1 _Bool literal_control_chars;
0.00 41 1 _Bool artificial;
0.00 48 8 char* arg_text;
<SNIP>

Reviewed-by: Ian Rogers <irogers@google.com>
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: 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/20240411033256.2099646-5-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
d001c7a7 9b561be1

+324 -5
+26 -4
tools/perf/builtin-annotate.c
··· 469 469 goto find_next; 470 470 } 471 471 472 - hist_entry__annotate_data_tty(he, evsel); 473 - goto find_next; 472 + if (use_browser == 1) 473 + key = hist_entry__annotate_data_tui(he, evsel, NULL); 474 + else 475 + key = hist_entry__annotate_data_tty(he, evsel); 476 + 477 + switch (key) { 478 + case -1: 479 + if (!ann->skip_missing) 480 + return; 481 + /* fall through */ 482 + case K_RIGHT: 483 + case '>': 484 + next = rb_next(nd); 485 + break; 486 + case K_LEFT: 487 + case '<': 488 + next = rb_prev(nd); 489 + break; 490 + default: 491 + return; 492 + } 493 + 494 + if (next != NULL) 495 + nd = next; 496 + 497 + continue; 474 498 } 475 499 476 500 if (use_browser == 2) { ··· 897 873 use_browser = 2; 898 874 #endif 899 875 900 - /* FIXME: only support stdio for now */ 901 876 if (annotate.data_type) { 902 - use_browser = 0; 903 877 annotate_opts.annotate_src = false; 904 878 symbol_conf.annotate_data_member = true; 905 879 symbol_conf.annotate_data_sample = true;
+1
tools/perf/ui/browsers/Build
··· 1 1 perf-y += annotate.o 2 + perf-y += annotate-data.o 2 3 perf-y += hists.o 3 4 perf-y += map.o 4 5 perf-y += scripts.o
+282
tools/perf/ui/browsers/annotate-data.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <inttypes.h> 3 + #include <string.h> 4 + #include <sys/ttydefaults.h> 5 + 6 + #include "ui/browser.h" 7 + #include "ui/helpline.h" 8 + #include "ui/keysyms.h" 9 + #include "ui/ui.h" 10 + #include "util/annotate.h" 11 + #include "util/annotate-data.h" 12 + #include "util/evsel.h" 13 + #include "util/sort.h" 14 + 15 + struct annotated_data_browser { 16 + struct ui_browser b; 17 + struct list_head entries; 18 + }; 19 + 20 + struct browser_entry { 21 + struct list_head node; 22 + struct annotated_member *data; 23 + struct type_hist_entry hists; 24 + int indent; 25 + }; 26 + 27 + static void update_hist_entry(struct type_hist_entry *dst, 28 + struct type_hist_entry *src) 29 + { 30 + dst->nr_samples += src->nr_samples; 31 + dst->period += src->period; 32 + } 33 + 34 + static int get_member_overhead(struct annotated_data_type *adt, 35 + struct browser_entry *entry, 36 + struct evsel *evsel) 37 + { 38 + struct annotated_member *member = entry->data; 39 + int i; 40 + 41 + for (i = 0; i < member->size; i++) { 42 + struct type_hist *h; 43 + int offset = member->offset + i; 44 + 45 + h = adt->histograms[evsel->core.idx]; 46 + update_hist_entry(&entry->hists, &h->addr[offset]); 47 + } 48 + return 0; 49 + } 50 + 51 + static int add_child_entries(struct annotated_data_browser *browser, 52 + struct annotated_data_type *adt, 53 + struct annotated_member *member, 54 + struct evsel *evsel, int indent) 55 + { 56 + struct annotated_member *pos; 57 + struct browser_entry *entry; 58 + int nr_entries = 0; 59 + 60 + entry = zalloc(sizeof(*entry)); 61 + if (entry == NULL) 62 + return -1; 63 + 64 + entry->data = member; 65 + entry->indent = indent; 66 + if (get_member_overhead(adt, entry, evsel) < 0) { 67 + free(entry); 68 + return -1; 69 + } 70 + 71 + list_add_tail(&entry->node, &browser->entries); 72 + nr_entries++; 73 + 74 + list_for_each_entry(pos, &member->children, node) { 75 + int nr = add_child_entries(browser, adt, pos, evsel, indent + 1); 76 + 77 + if (nr < 0) 78 + return nr; 79 + 80 + nr_entries += nr; 81 + } 82 + 83 + /* add an entry for the closing bracket ("}") */ 84 + if (!list_empty(&member->children)) { 85 + entry = zalloc(sizeof(*entry)); 86 + if (entry == NULL) 87 + return -1; 88 + 89 + entry->indent = indent; 90 + list_add_tail(&entry->node, &browser->entries); 91 + nr_entries++; 92 + } 93 + 94 + return nr_entries; 95 + } 96 + 97 + static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser) 98 + { 99 + struct hist_entry *he = browser->b.priv; 100 + struct annotated_data_type *adt = he->mem_type; 101 + struct evsel *evsel = hists_to_evsel(he->hists); 102 + 103 + INIT_LIST_HEAD(&browser->entries); 104 + browser->b.entries = &browser->entries; 105 + browser->b.nr_entries = add_child_entries(browser, adt, &adt->self, 106 + evsel, /*indent=*/0); 107 + return 0; 108 + } 109 + 110 + static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser) 111 + { 112 + struct browser_entry *pos, *tmp; 113 + 114 + list_for_each_entry_safe(pos, tmp, &browser->entries, node) { 115 + list_del_init(&pos->node); 116 + free(pos); 117 + } 118 + } 119 + 120 + static unsigned int browser__refresh(struct ui_browser *uib) 121 + { 122 + return ui_browser__list_head_refresh(uib); 123 + } 124 + 125 + static int browser__show(struct ui_browser *uib) 126 + { 127 + struct hist_entry *he = uib->priv; 128 + struct annotated_data_type *adt = he->mem_type; 129 + const char *help = "Press 'h' for help on key bindings"; 130 + char title[256]; 131 + 132 + snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)", 133 + adt->self.type_name, he->stat.nr_events); 134 + 135 + if (ui_browser__show(uib, title, help) < 0) 136 + return -1; 137 + 138 + /* second line header */ 139 + ui_browser__gotorc_title(uib, 0, 0); 140 + ui_browser__set_color(uib, HE_COLORSET_ROOT); 141 + 142 + if (symbol_conf.show_total_period) 143 + strcpy(title, "Period"); 144 + else if (symbol_conf.show_nr_samples) 145 + strcpy(title, "Samples"); 146 + else 147 + strcpy(title, "Percent"); 148 + 149 + ui_browser__printf(uib, " %10s %10s %10s %s", 150 + title, "Offset", "Size", "Field"); 151 + ui_browser__write_nstring(uib, "", uib->width); 152 + return 0; 153 + } 154 + 155 + static void browser__write_overhead(struct ui_browser *uib, 156 + struct type_hist *total, 157 + struct type_hist_entry *hist, int row) 158 + { 159 + u64 period = hist->period; 160 + double percent = total->period ? (100.0 * period / total->period) : 0; 161 + bool current = ui_browser__is_current_entry(uib, row); 162 + int nr_samples = 0; 163 + 164 + ui_browser__set_percent_color(uib, percent, current); 165 + 166 + if (symbol_conf.show_total_period) 167 + ui_browser__printf(uib, " %10" PRIu64, period); 168 + else if (symbol_conf.show_nr_samples) 169 + ui_browser__printf(uib, " %10d", nr_samples); 170 + else 171 + ui_browser__printf(uib, " %10.2f", percent); 172 + 173 + ui_browser__set_percent_color(uib, 0, current); 174 + } 175 + 176 + static void browser__write(struct ui_browser *uib, void *entry, int row) 177 + { 178 + struct browser_entry *be = entry; 179 + struct annotated_member *member = be->data; 180 + struct hist_entry *he = uib->priv; 181 + struct annotated_data_type *adt = he->mem_type; 182 + struct evsel *evsel = hists_to_evsel(he->hists); 183 + 184 + if (member == NULL) { 185 + bool current = ui_browser__is_current_entry(uib, row); 186 + 187 + /* print the closing bracket */ 188 + ui_browser__set_percent_color(uib, 0, current); 189 + ui_browser__write_nstring(uib, "", 11); 190 + ui_browser__printf(uib, " %10s %10s %*s};", 191 + "", "", be->indent * 4, ""); 192 + ui_browser__write_nstring(uib, "", uib->width); 193 + return; 194 + } 195 + 196 + /* print the number */ 197 + browser__write_overhead(uib, adt->histograms[evsel->core.idx], 198 + &be->hists, row); 199 + 200 + /* print type info */ 201 + if (be->indent == 0 && !member->var_name) { 202 + ui_browser__printf(uib, " %10d %10d %s%s", 203 + member->offset, member->size, 204 + member->type_name, 205 + list_empty(&member->children) ? ";" : " {"); 206 + } else { 207 + ui_browser__printf(uib, " %10d %10d %*s%s\t%s%s", 208 + member->offset, member->size, 209 + be->indent * 4, "", member->type_name, 210 + member->var_name ?: "", 211 + list_empty(&member->children) ? ";" : " {"); 212 + } 213 + /* fill the rest */ 214 + ui_browser__write_nstring(uib, "", uib->width); 215 + } 216 + 217 + static int annotated_data_browser__run(struct annotated_data_browser *browser, 218 + struct evsel *evsel __maybe_unused, 219 + struct hist_browser_timer *hbt) 220 + { 221 + int delay_secs = hbt ? hbt->refresh : 0; 222 + int key; 223 + 224 + if (browser__show(&browser->b) < 0) 225 + return -1; 226 + 227 + while (1) { 228 + key = ui_browser__run(&browser->b, delay_secs); 229 + 230 + switch (key) { 231 + case K_TIMER: 232 + if (hbt) 233 + hbt->timer(hbt->arg); 234 + continue; 235 + case K_F1: 236 + case 'h': 237 + ui_browser__help_window(&browser->b, 238 + "UP/DOWN/PGUP\n" 239 + "PGDN/SPACE Navigate\n" 240 + "</> Move to prev/next symbol\n" 241 + "q/ESC/CTRL+C Exit\n\n"); 242 + continue; 243 + case K_LEFT: 244 + case '<': 245 + case '>': 246 + case K_ESC: 247 + case 'q': 248 + case CTRL('c'): 249 + goto out; 250 + default: 251 + continue; 252 + } 253 + } 254 + out: 255 + ui_browser__hide(&browser->b); 256 + return key; 257 + } 258 + 259 + int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel, 260 + struct hist_browser_timer *hbt) 261 + { 262 + struct annotated_data_browser browser = { 263 + .b = { 264 + .refresh = browser__refresh, 265 + .seek = ui_browser__list_head_seek, 266 + .write = browser__write, 267 + .priv = he, 268 + .extra_title_lines = 1, 269 + }, 270 + }; 271 + int ret; 272 + 273 + ui_helpline__push("Press ESC to exit"); 274 + 275 + ret = annotated_data_browser__collect_entries(&browser); 276 + if (ret == 0) 277 + ret = annotated_data_browser__run(&browser, evsel, hbt); 278 + 279 + annotated_data_browser__delete_entries(&browser); 280 + 281 + return ret; 282 + }
+2 -1
tools/perf/util/annotate-data.c
··· 1820 1820 print_annotated_data_type(he->mem_type, &he->mem_type->self, evsel, 0); 1821 1821 printf("\n"); 1822 1822 1823 - return 0; 1823 + /* move to the next entry */ 1824 + return '>'; 1824 1825 }
+13
tools/perf/util/annotate-data.h
··· 10 10 struct annotated_op_loc; 11 11 struct debuginfo; 12 12 struct evsel; 13 + struct hist_browser_timer; 13 14 struct hist_entry; 14 15 struct map_symbol; 15 16 struct thread; ··· 193 192 } 194 193 195 194 #endif /* HAVE_DWARF_SUPPORT */ 195 + 196 + #ifdef HAVE_SLANG_SUPPORT 197 + int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel, 198 + struct hist_browser_timer *hbt); 199 + #else 200 + static inline int hist_entry__annotate_data_tui(struct hist_entry *he __maybe_unused, 201 + struct evsel *evsel __maybe_unused, 202 + struct hist_browser_timer *hbt __maybe_unused) 203 + { 204 + return -1; 205 + } 206 + #endif /* HAVE_SLANG_SUPPORT */ 196 207 197 208 #endif /* _PERF_ANNOTATE_DATA_H */