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 #define FOLDED_SIGN '+' 18 #define UNFOLD_SIGN '-' 19 #define NOCHLD_SIGN ' ' 20 21 struct browser_entry { 22 struct list_head node; 23 struct annotated_member *data; 24 struct type_hist_entry *hists; 25 struct browser_entry *parent; 26 struct list_head children; 27 int indent; /*indentation level, starts from 0 */ 28 int nr_entries; /* # of visible entries: self + descendents */ 29 bool folded; /* only can be false when it has children */ 30 }; 31 32 struct annotated_data_browser { 33 struct ui_browser b; 34 struct list_head entries; 35 struct browser_entry *curr; 36 int nr_events; 37 }; 38 39 static struct annotated_data_browser *get_browser(struct ui_browser *uib) 40 { 41 return container_of(uib, struct annotated_data_browser, b); 42 } 43 44 static void update_hist_entry(struct type_hist_entry *dst, 45 struct type_hist_entry *src) 46 { 47 dst->nr_samples += src->nr_samples; 48 dst->period += src->period; 49 } 50 51 static int get_member_overhead(struct annotated_data_type *adt, 52 struct browser_entry *entry, 53 struct evsel *leader) 54 { 55 struct annotated_member *member = entry->data; 56 int i, k; 57 58 for (i = 0; i < member->size; i++) { 59 struct type_hist *h; 60 struct evsel *evsel; 61 int offset = member->offset + i; 62 63 k = 0; 64 for_each_group_evsel(evsel, leader) { 65 if (symbol_conf.skip_empty && 66 evsel__hists(evsel)->stats.nr_samples == 0) 67 continue; 68 69 h = adt->histograms[evsel->core.idx]; 70 update_hist_entry(&entry->hists[k++], &h->addr[offset]); 71 } 72 } 73 return 0; 74 } 75 76 static int add_child_entries(struct annotated_data_browser *browser, 77 struct browser_entry *parent, 78 struct annotated_data_type *adt, 79 struct annotated_member *member, 80 struct evsel *evsel, int indent) 81 { 82 struct annotated_member *pos; 83 struct browser_entry *entry; 84 struct list_head *parent_list; 85 86 entry = zalloc(sizeof(*entry)); 87 if (entry == NULL) 88 return -1; 89 90 entry->hists = calloc(browser->nr_events, sizeof(*entry->hists)); 91 if (entry->hists == NULL) { 92 free(entry); 93 return -1; 94 } 95 96 entry->data = member; 97 entry->parent = parent; 98 entry->indent = indent; 99 if (get_member_overhead(adt, entry, evsel) < 0) { 100 free(entry); 101 return -1; 102 } 103 104 INIT_LIST_HEAD(&entry->children); 105 if (parent) 106 parent_list = &parent->children; 107 else 108 parent_list = &browser->entries; 109 110 list_add_tail(&entry->node, parent_list); 111 112 list_for_each_entry(pos, &member->children, node) { 113 int nr = add_child_entries(browser, entry, adt, pos, evsel, 114 indent + 1); 115 if (nr < 0) 116 return nr; 117 } 118 119 /* add an entry for the closing bracket ("}") */ 120 if (!list_empty(&member->children)) { 121 struct browser_entry *bracket; 122 123 bracket = zalloc(sizeof(*bracket)); 124 if (bracket == NULL) 125 return -1; 126 127 bracket->indent = indent; 128 bracket->parent = entry; 129 bracket->folded = true; 130 bracket->nr_entries = 1; 131 132 INIT_LIST_HEAD(&bracket->children); 133 list_add_tail(&bracket->node, &entry->children); 134 } 135 136 /* fold child entries by default */ 137 entry->folded = true; 138 entry->nr_entries = 1; 139 return 0; 140 } 141 142 static u32 count_visible_entries(struct annotated_data_browser *browser) 143 { 144 int nr = 0; 145 struct browser_entry *entry; 146 147 list_for_each_entry(entry, &browser->entries, node) 148 nr += entry->nr_entries; 149 150 return nr; 151 } 152 153 static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser) 154 { 155 struct hist_entry *he = browser->b.priv; 156 struct annotated_data_type *adt = he->mem_type; 157 struct evsel *evsel = hists_to_evsel(he->hists); 158 159 INIT_LIST_HEAD(&browser->entries); 160 161 add_child_entries(browser, /*parent=*/NULL, adt, &adt->self, evsel, 162 /*indent=*/0); 163 164 browser->b.entries = &browser->entries; 165 browser->b.nr_entries = count_visible_entries(browser); 166 return 0; 167 } 168 169 static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser) 170 { 171 struct browser_entry *pos, *tmp; 172 173 list_for_each_entry_safe(pos, tmp, &browser->entries, node) { 174 list_del_init(&pos->node); 175 zfree(&pos->hists); 176 free(pos); 177 } 178 } 179 180 static struct browser_entry *get_first_child(struct browser_entry *entry) 181 { 182 if (list_empty(&entry->children)) 183 return NULL; 184 185 return list_first_entry(&entry->children, struct browser_entry, node); 186 } 187 188 static struct browser_entry *get_last_child(struct browser_entry *entry) 189 { 190 if (list_empty(&entry->children)) 191 return NULL; 192 193 return list_last_entry(&entry->children, struct browser_entry, node); 194 } 195 196 static bool is_first_child(struct browser_entry *entry) 197 { 198 /* This will be checked in a different way */ 199 if (entry->parent == NULL) 200 return false; 201 202 return get_first_child(entry->parent) == entry; 203 } 204 205 static bool is_last_child(struct browser_entry *entry) 206 { 207 /* This will be checked in a different way */ 208 if (entry->parent == NULL) 209 return false; 210 211 return get_last_child(entry->parent) == entry; 212 } 213 214 static struct browser_entry *browser__prev_entry(struct ui_browser *uib, 215 struct browser_entry *entry) 216 { 217 struct annotated_data_browser *browser = get_browser(uib); 218 struct browser_entry *first; 219 220 first = list_first_entry(&browser->entries, struct browser_entry, node); 221 222 while (entry != first) { 223 if (is_first_child(entry)) 224 entry = entry->parent; 225 else { 226 entry = list_prev_entry(entry, node); 227 while (!entry->folded) 228 entry = get_last_child(entry); 229 } 230 231 if (!uib->filter || !uib->filter(uib, &entry->node)) 232 return entry; 233 } 234 return first; 235 } 236 237 static struct browser_entry *browser__next_entry(struct ui_browser *uib, 238 struct browser_entry *entry) 239 { 240 struct annotated_data_browser *browser = get_browser(uib); 241 struct browser_entry *last; 242 243 last = list_last_entry(&browser->entries, struct browser_entry, node); 244 while (!last->folded) 245 last = get_last_child(last); 246 247 while (entry != last) { 248 if (!entry->folded) 249 entry = get_first_child(entry); 250 else { 251 while (is_last_child(entry)) 252 entry = entry->parent; 253 254 entry = list_next_entry(entry, node); 255 } 256 257 if (!uib->filter || !uib->filter(uib, &entry->node)) 258 return entry; 259 } 260 return last; 261 } 262 263 static void browser__seek(struct ui_browser *uib, off_t offset, int whence) 264 { 265 struct annotated_data_browser *browser = get_browser(uib); 266 struct browser_entry *entry; 267 268 if (uib->nr_entries == 0) 269 return; 270 271 switch (whence) { 272 case SEEK_SET: 273 entry = list_first_entry(&browser->entries, typeof(*entry), node); 274 if (uib->filter && uib->filter(uib, &entry->node)) 275 entry = browser__next_entry(uib, entry); 276 break; 277 case SEEK_CUR: 278 entry = list_entry(uib->top, typeof(*entry), node); 279 break; 280 case SEEK_END: 281 entry = list_last_entry(&browser->entries, typeof(*entry), node); 282 while (!entry->folded) 283 entry = get_last_child(entry); 284 if (uib->filter && uib->filter(uib, &entry->node)) 285 entry = browser__prev_entry(uib, entry); 286 break; 287 default: 288 return; 289 } 290 291 assert(entry != NULL); 292 293 if (offset > 0) { 294 while (offset-- != 0) 295 entry = browser__next_entry(uib, entry); 296 } else { 297 while (offset++ != 0) 298 entry = browser__prev_entry(uib, entry); 299 } 300 301 uib->top = &entry->node; 302 } 303 304 static unsigned int browser__refresh(struct ui_browser *uib) 305 { 306 struct annotated_data_browser *browser = get_browser(uib); 307 struct browser_entry *entry, *next; 308 int row = 0; 309 310 if (uib->top == NULL || uib->top == uib->entries) 311 browser__seek(uib, SEEK_SET, 0); 312 313 entry = list_entry(uib->top, typeof(*entry), node); 314 315 while (true) { 316 if (!uib->filter || !uib->filter(uib, &entry->node)) { 317 ui_browser__gotorc(uib, row, 0); 318 uib->write(uib, entry, row); 319 if (uib->top_idx + row == uib->index) 320 browser->curr = entry; 321 if (++row == uib->rows) 322 break; 323 } 324 next = browser__next_entry(uib, entry); 325 if (next == entry) 326 break; 327 328 entry = next; 329 } 330 331 return row; 332 } 333 334 static int browser__show(struct ui_browser *uib) 335 { 336 struct hist_entry *he = uib->priv; 337 struct annotated_data_type *adt = he->mem_type; 338 struct annotated_data_browser *browser = get_browser(uib); 339 const char *help = "Press 'h' for help on key bindings"; 340 char title[256]; 341 342 snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)", 343 adt->self.type_name, he->stat.nr_events); 344 345 if (ui_browser__show(uib, title, help) < 0) 346 return -1; 347 348 /* second line header */ 349 ui_browser__gotorc_title(uib, 0, 0); 350 ui_browser__set_color(uib, HE_COLORSET_ROOT); 351 352 if (symbol_conf.show_total_period) 353 strcpy(title, "Period"); 354 else if (symbol_conf.show_nr_samples) 355 strcpy(title, "Samples"); 356 else 357 strcpy(title, "Percent"); 358 359 ui_browser__printf(uib, "%*s %10s %10s %10s %s", 360 2 + 11 * (browser->nr_events - 1), "", 361 title, "Offset", "Size", "Field"); 362 ui_browser__write_nstring(uib, "", uib->width); 363 return 0; 364 } 365 366 static void browser__write_overhead(struct ui_browser *uib, 367 struct type_hist *total, 368 struct type_hist_entry *hist, int row) 369 { 370 u64 period = hist->period; 371 double percent = total->period ? (100.0 * period / total->period) : 0; 372 bool current = ui_browser__is_current_entry(uib, row); 373 int nr_samples = 0; 374 375 ui_browser__set_percent_color(uib, percent, current); 376 377 if (symbol_conf.show_total_period) 378 ui_browser__printf(uib, " %10" PRIu64, period); 379 else if (symbol_conf.show_nr_samples) 380 ui_browser__printf(uib, " %10d", nr_samples); 381 else 382 ui_browser__printf(uib, " %10.2f", percent); 383 384 ui_browser__set_percent_color(uib, 0, current); 385 } 386 387 static void browser__write(struct ui_browser *uib, void *entry, int row) 388 { 389 struct annotated_data_browser *browser = get_browser(uib); 390 struct browser_entry *be = entry; 391 struct annotated_member *member = be->data; 392 struct hist_entry *he = uib->priv; 393 struct annotated_data_type *adt = he->mem_type; 394 struct evsel *leader = hists_to_evsel(he->hists); 395 struct evsel *evsel; 396 int idx = 0; 397 bool current = ui_browser__is_current_entry(uib, row); 398 399 if (member == NULL) { 400 /* print the closing bracket */ 401 ui_browser__set_percent_color(uib, 0, current); 402 ui_browser__printf(uib, "%c ", NOCHLD_SIGN); 403 ui_browser__write_nstring(uib, "", 11 * browser->nr_events); 404 ui_browser__printf(uib, " %10s %10s %*s};", 405 "", "", be->indent * 4, ""); 406 ui_browser__write_nstring(uib, "", uib->width); 407 return; 408 } 409 410 ui_browser__set_percent_color(uib, 0, current); 411 412 if (!list_empty(&be->children)) 413 ui_browser__printf(uib, "%c ", be->folded ? FOLDED_SIGN : UNFOLD_SIGN); 414 else 415 ui_browser__printf(uib, "%c ", NOCHLD_SIGN); 416 417 /* print the number */ 418 for_each_group_evsel(evsel, leader) { 419 struct type_hist *h = adt->histograms[evsel->core.idx]; 420 421 if (symbol_conf.skip_empty && 422 evsel__hists(evsel)->stats.nr_samples == 0) 423 continue; 424 425 browser__write_overhead(uib, h, &be->hists[idx++], row); 426 } 427 428 /* print type info */ 429 if (be->indent == 0 && !member->var_name) { 430 ui_browser__printf(uib, " %#10x %#10x %s%s", 431 member->offset, member->size, 432 member->type_name, 433 list_empty(&member->children) || be->folded? ";" : " {"); 434 } else { 435 ui_browser__printf(uib, " %#10x %#10x %*s%s\t%s%s", 436 member->offset, member->size, 437 be->indent * 4, "", member->type_name, 438 member->var_name ?: "", 439 list_empty(&member->children) || be->folded ? ";" : " {"); 440 } 441 /* fill the rest */ 442 ui_browser__write_nstring(uib, "", uib->width); 443 } 444 445 static void annotated_data_browser__fold(struct annotated_data_browser *browser, 446 struct browser_entry *entry, 447 bool recursive) 448 { 449 struct browser_entry *child; 450 451 if (list_empty(&entry->children)) 452 return; 453 if (entry->folded && !recursive) 454 return; 455 456 if (recursive) { 457 list_for_each_entry(child, &entry->children, node) 458 annotated_data_browser__fold(browser, child, true); 459 } 460 461 entry->nr_entries = 1; 462 entry->folded = true; 463 } 464 465 static void annotated_data_browser__unfold(struct annotated_data_browser *browser, 466 struct browser_entry *entry, 467 bool recursive) 468 { 469 struct browser_entry *child; 470 int nr_entries; 471 472 if (list_empty(&entry->children)) 473 return; 474 if (!entry->folded && !recursive) 475 return; 476 477 nr_entries = 1; /* for self */ 478 list_for_each_entry(child, &entry->children, node) { 479 if (recursive) 480 annotated_data_browser__unfold(browser, child, true); 481 482 nr_entries += child->nr_entries; 483 } 484 485 entry->nr_entries = nr_entries; 486 entry->folded = false; 487 } 488 489 static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser, 490 bool recursive) 491 { 492 struct browser_entry *curr = browser->curr; 493 struct browser_entry *parent; 494 495 parent = curr->parent; 496 while (parent) { 497 parent->nr_entries -= curr->nr_entries; 498 parent = parent->parent; 499 } 500 browser->b.nr_entries -= curr->nr_entries; 501 502 if (curr->folded) 503 annotated_data_browser__unfold(browser, curr, recursive); 504 else 505 annotated_data_browser__fold(browser, curr, recursive); 506 507 parent = curr->parent; 508 while (parent) { 509 parent->nr_entries += curr->nr_entries; 510 parent = parent->parent; 511 } 512 browser->b.nr_entries += curr->nr_entries; 513 514 assert(browser->b.nr_entries == count_visible_entries(browser)); 515 } 516 517 static int annotated_data_browser__run(struct annotated_data_browser *browser, 518 struct evsel *evsel __maybe_unused, 519 struct hist_browser_timer *hbt) 520 { 521 int delay_secs = hbt ? hbt->refresh : 0; 522 int key; 523 524 if (browser__show(&browser->b) < 0) 525 return -1; 526 527 while (1) { 528 key = ui_browser__run(&browser->b, delay_secs); 529 530 switch (key) { 531 case K_TIMER: 532 if (hbt) 533 hbt->timer(hbt->arg); 534 continue; 535 case K_F1: 536 case 'h': 537 ui_browser__help_window(&browser->b, 538 "UP/DOWN/PGUP\n" 539 "PGDN/SPACE Navigate\n" 540 "</> Move to prev/next symbol\n" 541 "e Expand/Collapse current entry\n" 542 "E Expand/Collapse all children of the current\n" 543 "q/ESC/CTRL+C Exit\n\n"); 544 continue; 545 case 'e': 546 annotated_data_browser__toggle_fold(browser, 547 /*recursive=*/false); 548 break; 549 case 'E': 550 annotated_data_browser__toggle_fold(browser, 551 /*recursive=*/true); 552 break; 553 case K_LEFT: 554 case '<': 555 case '>': 556 case K_ESC: 557 case 'q': 558 case CTRL('c'): 559 goto out; 560 default: 561 continue; 562 } 563 } 564 out: 565 ui_browser__hide(&browser->b); 566 return key; 567 } 568 569 int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel, 570 struct hist_browser_timer *hbt) 571 { 572 struct annotated_data_browser browser = { 573 .b = { 574 .refresh = browser__refresh, 575 .seek = browser__seek, 576 .write = browser__write, 577 .priv = he, 578 .extra_title_lines = 1, 579 }, 580 .nr_events = 1, 581 }; 582 int ret; 583 584 ui_helpline__push("Press ESC to exit"); 585 586 if (evsel__is_group_event(evsel)) { 587 struct evsel *pos; 588 int nr = 0; 589 590 for_each_group_evsel(pos, evsel) { 591 if (!symbol_conf.skip_empty || 592 evsel__hists(pos)->stats.nr_samples) 593 nr++; 594 } 595 browser.nr_events = nr; 596 } 597 598 ret = annotated_data_browser__collect_entries(&browser); 599 if (ret < 0) 600 goto out; 601 602 /* To get the top and current entry */ 603 browser__refresh(&browser.b); 604 /* Show the first-level child entries by default */ 605 annotated_data_browser__toggle_fold(&browser, /*recursive=*/false); 606 607 ret = annotated_data_browser__run(&browser, evsel, hbt); 608 609 out: 610 annotated_data_browser__delete_entries(&browser); 611 612 return ret; 613 } 614