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