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