1 // SPDX-License-Identifier: GPL-2.0 2 #include <inttypes.h> 3 #include <string.h> 4 #include <linux/zalloc.h> 5 #include <sys/ttydefaults.h> 6 7 #include "ui/browser.h" 8 #include "ui/helpline.h" 9 #include "ui/keysyms.h" 10 #include "ui/ui.h" 11 #include "util/annotate.h" 12 #include "util/annotate-data.h" 13 #include "util/evsel.h" 14 #include "util/evlist.h" 15 #include "util/sort.h" 16 17 struct annotated_data_browser { 18 struct ui_browser b; 19 struct list_head entries; 20 int nr_events; 21 }; 22 23 struct browser_entry { 24 struct list_head node; 25 struct annotated_member *data; 26 struct type_hist_entry *hists; 27 int indent; 28 }; 29 30 static struct annotated_data_browser *get_browser(struct ui_browser *uib) 31 { 32 return container_of(uib, struct annotated_data_browser, b); 33 } 34 35 static void update_hist_entry(struct type_hist_entry *dst, 36 struct type_hist_entry *src) 37 { 38 dst->nr_samples += src->nr_samples; 39 dst->period += src->period; 40 } 41 42 static int get_member_overhead(struct annotated_data_type *adt, 43 struct browser_entry *entry, 44 struct evsel *leader) 45 { 46 struct annotated_member *member = entry->data; 47 int i, k; 48 49 for (i = 0; i < member->size; i++) { 50 struct type_hist *h; 51 struct evsel *evsel; 52 int offset = member->offset + i; 53 54 k = 0; 55 for_each_group_evsel(evsel, leader) { 56 if (symbol_conf.skip_empty && 57 evsel__hists(evsel)->stats.nr_samples == 0) 58 continue; 59 60 h = adt->histograms[evsel->core.idx]; 61 update_hist_entry(&entry->hists[k++], &h->addr[offset]); 62 } 63 } 64 return 0; 65 } 66 67 static int add_child_entries(struct annotated_data_browser *browser, 68 struct annotated_data_type *adt, 69 struct annotated_member *member, 70 struct evsel *evsel, int indent) 71 { 72 struct annotated_member *pos; 73 struct browser_entry *entry; 74 int nr_entries = 0; 75 76 entry = zalloc(sizeof(*entry)); 77 if (entry == NULL) 78 return -1; 79 80 entry->hists = calloc(browser->nr_events, sizeof(*entry->hists)); 81 if (entry->hists == NULL) { 82 free(entry); 83 return -1; 84 } 85 86 entry->data = member; 87 entry->indent = indent; 88 if (get_member_overhead(adt, entry, evsel) < 0) { 89 free(entry); 90 return -1; 91 } 92 93 list_add_tail(&entry->node, &browser->entries); 94 nr_entries++; 95 96 list_for_each_entry(pos, &member->children, node) { 97 int nr = add_child_entries(browser, adt, pos, evsel, indent + 1); 98 99 if (nr < 0) 100 return nr; 101 102 nr_entries += nr; 103 } 104 105 /* add an entry for the closing bracket ("}") */ 106 if (!list_empty(&member->children)) { 107 entry = zalloc(sizeof(*entry)); 108 if (entry == NULL) 109 return -1; 110 111 entry->indent = indent; 112 list_add_tail(&entry->node, &browser->entries); 113 nr_entries++; 114 } 115 116 return nr_entries; 117 } 118 119 static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser) 120 { 121 struct hist_entry *he = browser->b.priv; 122 struct annotated_data_type *adt = he->mem_type; 123 struct evsel *evsel = hists_to_evsel(he->hists); 124 125 INIT_LIST_HEAD(&browser->entries); 126 browser->b.entries = &browser->entries; 127 browser->b.nr_entries = add_child_entries(browser, adt, &adt->self, 128 evsel, /*indent=*/0); 129 return 0; 130 } 131 132 static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser) 133 { 134 struct browser_entry *pos, *tmp; 135 136 list_for_each_entry_safe(pos, tmp, &browser->entries, node) { 137 list_del_init(&pos->node); 138 zfree(&pos->hists); 139 free(pos); 140 } 141 } 142 143 static unsigned int browser__refresh(struct ui_browser *uib) 144 { 145 return ui_browser__list_head_refresh(uib); 146 } 147 148 static int browser__show(struct ui_browser *uib) 149 { 150 struct hist_entry *he = uib->priv; 151 struct annotated_data_type *adt = he->mem_type; 152 struct annotated_data_browser *browser = get_browser(uib); 153 const char *help = "Press 'h' for help on key bindings"; 154 char title[256]; 155 156 snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)", 157 adt->self.type_name, he->stat.nr_events); 158 159 if (ui_browser__show(uib, title, help) < 0) 160 return -1; 161 162 /* second line header */ 163 ui_browser__gotorc_title(uib, 0, 0); 164 ui_browser__set_color(uib, HE_COLORSET_ROOT); 165 166 if (symbol_conf.show_total_period) 167 strcpy(title, "Period"); 168 else if (symbol_conf.show_nr_samples) 169 strcpy(title, "Samples"); 170 else 171 strcpy(title, "Percent"); 172 173 ui_browser__printf(uib, "%*s %10s %10s %10s %s", 174 11 * (browser->nr_events - 1), "", 175 title, "Offset", "Size", "Field"); 176 ui_browser__write_nstring(uib, "", uib->width); 177 return 0; 178 } 179 180 static void browser__write_overhead(struct ui_browser *uib, 181 struct type_hist *total, 182 struct type_hist_entry *hist, int row) 183 { 184 u64 period = hist->period; 185 double percent = total->period ? (100.0 * period / total->period) : 0; 186 bool current = ui_browser__is_current_entry(uib, row); 187 int nr_samples = 0; 188 189 ui_browser__set_percent_color(uib, percent, current); 190 191 if (symbol_conf.show_total_period) 192 ui_browser__printf(uib, " %10" PRIu64, period); 193 else if (symbol_conf.show_nr_samples) 194 ui_browser__printf(uib, " %10d", nr_samples); 195 else 196 ui_browser__printf(uib, " %10.2f", percent); 197 198 ui_browser__set_percent_color(uib, 0, current); 199 } 200 201 static void browser__write(struct ui_browser *uib, void *entry, int row) 202 { 203 struct annotated_data_browser *browser = get_browser(uib); 204 struct browser_entry *be = entry; 205 struct annotated_member *member = be->data; 206 struct hist_entry *he = uib->priv; 207 struct annotated_data_type *adt = he->mem_type; 208 struct evsel *leader = hists_to_evsel(he->hists); 209 struct evsel *evsel; 210 int idx = 0; 211 212 if (member == NULL) { 213 bool current = ui_browser__is_current_entry(uib, row); 214 215 /* print the closing bracket */ 216 ui_browser__set_percent_color(uib, 0, current); 217 ui_browser__write_nstring(uib, "", 11 * browser->nr_events); 218 ui_browser__printf(uib, " %10s %10s %*s};", 219 "", "", be->indent * 4, ""); 220 ui_browser__write_nstring(uib, "", uib->width); 221 return; 222 } 223 224 /* print the number */ 225 for_each_group_evsel(evsel, leader) { 226 struct type_hist *h = adt->histograms[evsel->core.idx]; 227 228 if (symbol_conf.skip_empty && 229 evsel__hists(evsel)->stats.nr_samples == 0) 230 continue; 231 232 browser__write_overhead(uib, h, &be->hists[idx++], row); 233 } 234 235 /* print type info */ 236 if (be->indent == 0 && !member->var_name) { 237 ui_browser__printf(uib, " %10d %10d %s%s", 238 member->offset, member->size, 239 member->type_name, 240 list_empty(&member->children) ? ";" : " {"); 241 } else { 242 ui_browser__printf(uib, " %10d %10d %*s%s\t%s%s", 243 member->offset, member->size, 244 be->indent * 4, "", member->type_name, 245 member->var_name ?: "", 246 list_empty(&member->children) ? ";" : " {"); 247 } 248 /* fill the rest */ 249 ui_browser__write_nstring(uib, "", uib->width); 250 } 251 252 static int annotated_data_browser__run(struct annotated_data_browser *browser, 253 struct evsel *evsel __maybe_unused, 254 struct hist_browser_timer *hbt) 255 { 256 int delay_secs = hbt ? hbt->refresh : 0; 257 int key; 258 259 if (browser__show(&browser->b) < 0) 260 return -1; 261 262 while (1) { 263 key = ui_browser__run(&browser->b, delay_secs); 264 265 switch (key) { 266 case K_TIMER: 267 if (hbt) 268 hbt->timer(hbt->arg); 269 continue; 270 case K_F1: 271 case 'h': 272 ui_browser__help_window(&browser->b, 273 "UP/DOWN/PGUP\n" 274 "PGDN/SPACE Navigate\n" 275 "</> Move to prev/next symbol\n" 276 "q/ESC/CTRL+C Exit\n\n"); 277 continue; 278 case K_LEFT: 279 case '<': 280 case '>': 281 case K_ESC: 282 case 'q': 283 case CTRL('c'): 284 goto out; 285 default: 286 continue; 287 } 288 } 289 out: 290 ui_browser__hide(&browser->b); 291 return key; 292 } 293 294 int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel, 295 struct hist_browser_timer *hbt) 296 { 297 struct annotated_data_browser browser = { 298 .b = { 299 .refresh = browser__refresh, 300 .seek = ui_browser__list_head_seek, 301 .write = browser__write, 302 .priv = he, 303 .extra_title_lines = 1, 304 }, 305 .nr_events = 1, 306 }; 307 int ret; 308 309 ui_helpline__push("Press ESC to exit"); 310 311 if (evsel__is_group_event(evsel)) { 312 struct evsel *pos; 313 int nr = 0; 314 315 for_each_group_evsel(pos, evsel) { 316 if (!symbol_conf.skip_empty || 317 evsel__hists(pos)->stats.nr_samples) 318 nr++; 319 } 320 browser.nr_events = nr; 321 } 322 323 ret = annotated_data_browser__collect_entries(&browser); 324 if (ret == 0) 325 ret = annotated_data_browser__run(&browser, evsel, hbt); 326 327 annotated_data_browser__delete_entries(&browser); 328 329 return ret; 330 } 331