// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include "ui/browser.h" #include "ui/helpline.h" #include "ui/keysyms.h" #include "ui/ui.h" #include "util/annotate.h" #include "util/annotate-data.h" #include "util/evsel.h" #include "util/evlist.h" #include "util/sort.h" struct annotated_data_browser { struct ui_browser b; struct list_head entries; int nr_events; }; struct browser_entry { struct list_head node; struct annotated_member *data; struct type_hist_entry *hists; int indent; }; static struct annotated_data_browser *get_browser(struct ui_browser *uib) { return container_of(uib, struct annotated_data_browser, b); } static void update_hist_entry(struct type_hist_entry *dst, struct type_hist_entry *src) { dst->nr_samples += src->nr_samples; dst->period += src->period; } static int get_member_overhead(struct annotated_data_type *adt, struct browser_entry *entry, struct evsel *leader) { struct annotated_member *member = entry->data; int i, k; for (i = 0; i < member->size; i++) { struct type_hist *h; struct evsel *evsel; int offset = member->offset + i; for_each_group_evsel(evsel, leader) { h = adt->histograms[evsel->core.idx]; k = evsel__group_idx(evsel); update_hist_entry(&entry->hists[k], &h->addr[offset]); } } return 0; } static int add_child_entries(struct annotated_data_browser *browser, struct annotated_data_type *adt, struct annotated_member *member, struct evsel *evsel, int indent) { struct annotated_member *pos; struct browser_entry *entry; int nr_entries = 0; entry = zalloc(sizeof(*entry)); if (entry == NULL) return -1; entry->hists = calloc(browser->nr_events, sizeof(*entry->hists)); if (entry->hists == NULL) { free(entry); return -1; } entry->data = member; entry->indent = indent; if (get_member_overhead(adt, entry, evsel) < 0) { free(entry); return -1; } list_add_tail(&entry->node, &browser->entries); nr_entries++; list_for_each_entry(pos, &member->children, node) { int nr = add_child_entries(browser, adt, pos, evsel, indent + 1); if (nr < 0) return nr; nr_entries += nr; } /* add an entry for the closing bracket ("}") */ if (!list_empty(&member->children)) { entry = zalloc(sizeof(*entry)); if (entry == NULL) return -1; entry->indent = indent; list_add_tail(&entry->node, &browser->entries); nr_entries++; } return nr_entries; } static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser) { struct hist_entry *he = browser->b.priv; struct annotated_data_type *adt = he->mem_type; struct evsel *evsel = hists_to_evsel(he->hists); INIT_LIST_HEAD(&browser->entries); browser->b.entries = &browser->entries; browser->b.nr_entries = add_child_entries(browser, adt, &adt->self, evsel, /*indent=*/0); return 0; } static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser) { struct browser_entry *pos, *tmp; list_for_each_entry_safe(pos, tmp, &browser->entries, node) { list_del_init(&pos->node); zfree(&pos->hists); free(pos); } } static unsigned int browser__refresh(struct ui_browser *uib) { return ui_browser__list_head_refresh(uib); } static int browser__show(struct ui_browser *uib) { struct hist_entry *he = uib->priv; struct annotated_data_type *adt = he->mem_type; struct annotated_data_browser *browser = get_browser(uib); const char *help = "Press 'h' for help on key bindings"; char title[256]; snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)", adt->self.type_name, he->stat.nr_events); if (ui_browser__show(uib, title, help) < 0) return -1; /* second line header */ ui_browser__gotorc_title(uib, 0, 0); ui_browser__set_color(uib, HE_COLORSET_ROOT); if (symbol_conf.show_total_period) strcpy(title, "Period"); else if (symbol_conf.show_nr_samples) strcpy(title, "Samples"); else strcpy(title, "Percent"); ui_browser__printf(uib, "%*s %10s %10s %10s %s", 11 * (browser->nr_events - 1), "", title, "Offset", "Size", "Field"); ui_browser__write_nstring(uib, "", uib->width); return 0; } static void browser__write_overhead(struct ui_browser *uib, struct type_hist *total, struct type_hist_entry *hist, int row) { u64 period = hist->period; double percent = total->period ? (100.0 * period / total->period) : 0; bool current = ui_browser__is_current_entry(uib, row); int nr_samples = 0; ui_browser__set_percent_color(uib, percent, current); if (symbol_conf.show_total_period) ui_browser__printf(uib, " %10" PRIu64, period); else if (symbol_conf.show_nr_samples) ui_browser__printf(uib, " %10d", nr_samples); else ui_browser__printf(uib, " %10.2f", percent); ui_browser__set_percent_color(uib, 0, current); } static void browser__write(struct ui_browser *uib, void *entry, int row) { struct annotated_data_browser *browser = get_browser(uib); struct browser_entry *be = entry; struct annotated_member *member = be->data; struct hist_entry *he = uib->priv; struct annotated_data_type *adt = he->mem_type; struct evsel *leader = hists_to_evsel(he->hists); struct evsel *evsel; if (member == NULL) { bool current = ui_browser__is_current_entry(uib, row); /* print the closing bracket */ ui_browser__set_percent_color(uib, 0, current); ui_browser__write_nstring(uib, "", 11 * browser->nr_events); ui_browser__printf(uib, " %10s %10s %*s};", "", "", be->indent * 4, ""); ui_browser__write_nstring(uib, "", uib->width); return; } /* print the number */ for_each_group_evsel(evsel, leader) { struct type_hist *h = adt->histograms[evsel->core.idx]; int idx = evsel__group_idx(evsel); browser__write_overhead(uib, h, &be->hists[idx], row); } /* print type info */ if (be->indent == 0 && !member->var_name) { ui_browser__printf(uib, " %10d %10d %s%s", member->offset, member->size, member->type_name, list_empty(&member->children) ? ";" : " {"); } else { ui_browser__printf(uib, " %10d %10d %*s%s\t%s%s", member->offset, member->size, be->indent * 4, "", member->type_name, member->var_name ?: "", list_empty(&member->children) ? ";" : " {"); } /* fill the rest */ ui_browser__write_nstring(uib, "", uib->width); } static int annotated_data_browser__run(struct annotated_data_browser *browser, struct evsel *evsel __maybe_unused, struct hist_browser_timer *hbt) { int delay_secs = hbt ? hbt->refresh : 0; int key; if (browser__show(&browser->b) < 0) return -1; while (1) { key = ui_browser__run(&browser->b, delay_secs); switch (key) { case K_TIMER: if (hbt) hbt->timer(hbt->arg); continue; case K_F1: case 'h': ui_browser__help_window(&browser->b, "UP/DOWN/PGUP\n" "PGDN/SPACE Navigate\n" " Move to prev/next symbol\n" "q/ESC/CTRL+C Exit\n\n"); continue; case K_LEFT: case '<': case '>': case K_ESC: case 'q': case CTRL('c'): goto out; default: continue; } } out: ui_browser__hide(&browser->b); return key; } int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel, struct hist_browser_timer *hbt) { struct annotated_data_browser browser = { .b = { .refresh = browser__refresh, .seek = ui_browser__list_head_seek, .write = browser__write, .priv = he, .extra_title_lines = 1, }, .nr_events = 1, }; int ret; ui_helpline__push("Press ESC to exit"); if (evsel__is_group_event(evsel)) browser.nr_events = evsel->core.nr_members; ret = annotated_data_browser__collect_entries(&browser); if (ret == 0) ret = annotated_data_browser__run(&browser, evsel, hbt); annotated_data_browser__delete_entries(&browser); return ret; }