| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613 |
- // SPDX-License-Identifier: GPL-2.0
- #include <inttypes.h>
- #include <string.h>
- #include <linux/zalloc.h>
- #include <sys/ttydefaults.h>
- #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"
- #define FOLDED_SIGN '+'
- #define UNFOLD_SIGN '-'
- #define NOCHLD_SIGN ' '
- struct browser_entry {
- struct list_head node;
- struct annotated_member *data;
- struct type_hist_entry *hists;
- struct browser_entry *parent;
- struct list_head children;
- int indent; /*indentation level, starts from 0 */
- int nr_entries; /* # of visible entries: self + descendents */
- bool folded; /* only can be false when it has children */
- };
- struct annotated_data_browser {
- struct ui_browser b;
- struct list_head entries;
- struct browser_entry *curr;
- int nr_events;
- };
- 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;
- k = 0;
- for_each_group_evsel(evsel, leader) {
- if (symbol_conf.skip_empty &&
- evsel__hists(evsel)->stats.nr_samples == 0)
- continue;
- h = adt->histograms[evsel->core.idx];
- update_hist_entry(&entry->hists[k++], &h->addr[offset]);
- }
- }
- return 0;
- }
- static int add_child_entries(struct annotated_data_browser *browser,
- struct browser_entry *parent,
- struct annotated_data_type *adt,
- struct annotated_member *member,
- struct evsel *evsel, int indent)
- {
- struct annotated_member *pos;
- struct browser_entry *entry;
- struct list_head *parent_list;
- 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->parent = parent;
- entry->indent = indent;
- if (get_member_overhead(adt, entry, evsel) < 0) {
- free(entry);
- return -1;
- }
- INIT_LIST_HEAD(&entry->children);
- if (parent)
- parent_list = &parent->children;
- else
- parent_list = &browser->entries;
- list_add_tail(&entry->node, parent_list);
- list_for_each_entry(pos, &member->children, node) {
- int nr = add_child_entries(browser, entry, adt, pos, evsel,
- indent + 1);
- if (nr < 0)
- return nr;
- }
- /* add an entry for the closing bracket ("}") */
- if (!list_empty(&member->children)) {
- struct browser_entry *bracket;
- bracket = zalloc(sizeof(*bracket));
- if (bracket == NULL)
- return -1;
- bracket->indent = indent;
- bracket->parent = entry;
- bracket->folded = true;
- bracket->nr_entries = 1;
- INIT_LIST_HEAD(&bracket->children);
- list_add_tail(&bracket->node, &entry->children);
- }
- /* fold child entries by default */
- entry->folded = true;
- entry->nr_entries = 1;
- return 0;
- }
- static u32 count_visible_entries(struct annotated_data_browser *browser)
- {
- int nr = 0;
- struct browser_entry *entry;
- list_for_each_entry(entry, &browser->entries, node)
- nr += entry->nr_entries;
- return nr;
- }
- 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);
- add_child_entries(browser, /*parent=*/NULL, adt, &adt->self, evsel,
- /*indent=*/0);
- browser->b.entries = &browser->entries;
- browser->b.nr_entries = count_visible_entries(browser);
- 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 struct browser_entry *get_first_child(struct browser_entry *entry)
- {
- if (list_empty(&entry->children))
- return NULL;
- return list_first_entry(&entry->children, struct browser_entry, node);
- }
- static struct browser_entry *get_last_child(struct browser_entry *entry)
- {
- if (list_empty(&entry->children))
- return NULL;
- return list_last_entry(&entry->children, struct browser_entry, node);
- }
- static bool is_first_child(struct browser_entry *entry)
- {
- /* This will be checked in a different way */
- if (entry->parent == NULL)
- return false;
- return get_first_child(entry->parent) == entry;
- }
- static bool is_last_child(struct browser_entry *entry)
- {
- /* This will be checked in a different way */
- if (entry->parent == NULL)
- return false;
- return get_last_child(entry->parent) == entry;
- }
- static struct browser_entry *browser__prev_entry(struct ui_browser *uib,
- struct browser_entry *entry)
- {
- struct annotated_data_browser *browser = get_browser(uib);
- struct browser_entry *first;
- first = list_first_entry(&browser->entries, struct browser_entry, node);
- while (entry != first) {
- if (is_first_child(entry))
- entry = entry->parent;
- else {
- entry = list_prev_entry(entry, node);
- while (!entry->folded)
- entry = get_last_child(entry);
- }
- if (!uib->filter || !uib->filter(uib, &entry->node))
- return entry;
- }
- return first;
- }
- static struct browser_entry *browser__next_entry(struct ui_browser *uib,
- struct browser_entry *entry)
- {
- struct annotated_data_browser *browser = get_browser(uib);
- struct browser_entry *last;
- last = list_last_entry(&browser->entries, struct browser_entry, node);
- while (!last->folded)
- last = get_last_child(last);
- while (entry != last) {
- if (!entry->folded)
- entry = get_first_child(entry);
- else {
- while (is_last_child(entry))
- entry = entry->parent;
- entry = list_next_entry(entry, node);
- }
- if (!uib->filter || !uib->filter(uib, &entry->node))
- return entry;
- }
- return last;
- }
- static void browser__seek(struct ui_browser *uib, off_t offset, int whence)
- {
- struct annotated_data_browser *browser = get_browser(uib);
- struct browser_entry *entry;
- if (uib->nr_entries == 0)
- return;
- switch (whence) {
- case SEEK_SET:
- entry = list_first_entry(&browser->entries, typeof(*entry), node);
- if (uib->filter && uib->filter(uib, &entry->node))
- entry = browser__next_entry(uib, entry);
- break;
- case SEEK_CUR:
- entry = list_entry(uib->top, typeof(*entry), node);
- break;
- case SEEK_END:
- entry = list_last_entry(&browser->entries, typeof(*entry), node);
- while (!entry->folded)
- entry = get_last_child(entry);
- if (uib->filter && uib->filter(uib, &entry->node))
- entry = browser__prev_entry(uib, entry);
- break;
- default:
- return;
- }
- assert(entry != NULL);
- if (offset > 0) {
- while (offset-- != 0)
- entry = browser__next_entry(uib, entry);
- } else {
- while (offset++ != 0)
- entry = browser__prev_entry(uib, entry);
- }
- uib->top = &entry->node;
- }
- static unsigned int browser__refresh(struct ui_browser *uib)
- {
- struct annotated_data_browser *browser = get_browser(uib);
- struct browser_entry *entry, *next;
- int row = 0;
- if (uib->top == NULL || uib->top == uib->entries)
- browser__seek(uib, SEEK_SET, 0);
- entry = list_entry(uib->top, typeof(*entry), node);
- while (true) {
- if (!uib->filter || !uib->filter(uib, &entry->node)) {
- ui_browser__gotorc(uib, row, 0);
- uib->write(uib, entry, row);
- if (uib->top_idx + row == uib->index)
- browser->curr = entry;
- if (++row == uib->rows)
- break;
- }
- next = browser__next_entry(uib, entry);
- if (next == entry)
- break;
- entry = next;
- }
- return row;
- }
- 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",
- 2 + 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;
- int idx = 0;
- bool current = ui_browser__is_current_entry(uib, row);
- if (member == NULL) {
- /* print the closing bracket */
- ui_browser__set_percent_color(uib, 0, current);
- ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
- 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;
- }
- ui_browser__set_percent_color(uib, 0, current);
- if (!list_empty(&be->children))
- ui_browser__printf(uib, "%c ", be->folded ? FOLDED_SIGN : UNFOLD_SIGN);
- else
- ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
- /* print the number */
- for_each_group_evsel(evsel, leader) {
- struct type_hist *h = adt->histograms[evsel->core.idx];
- if (symbol_conf.skip_empty &&
- evsel__hists(evsel)->stats.nr_samples == 0)
- continue;
- browser__write_overhead(uib, h, &be->hists[idx++], row);
- }
- /* print type info */
- if (be->indent == 0 && !member->var_name) {
- ui_browser__printf(uib, " %#10x %#10x %s%s",
- member->offset, member->size,
- member->type_name,
- list_empty(&member->children) || be->folded? ";" : " {");
- } else {
- ui_browser__printf(uib, " %#10x %#10x %*s%s\t%s%s",
- member->offset, member->size,
- be->indent * 4, "", member->type_name,
- member->var_name ?: "",
- list_empty(&member->children) || be->folded ? ";" : " {");
- }
- /* fill the rest */
- ui_browser__write_nstring(uib, "", uib->width);
- }
- static void annotated_data_browser__fold(struct annotated_data_browser *browser,
- struct browser_entry *entry,
- bool recursive)
- {
- struct browser_entry *child;
- if (list_empty(&entry->children))
- return;
- if (entry->folded && !recursive)
- return;
- if (recursive) {
- list_for_each_entry(child, &entry->children, node)
- annotated_data_browser__fold(browser, child, true);
- }
- entry->nr_entries = 1;
- entry->folded = true;
- }
- static void annotated_data_browser__unfold(struct annotated_data_browser *browser,
- struct browser_entry *entry,
- bool recursive)
- {
- struct browser_entry *child;
- int nr_entries;
- if (list_empty(&entry->children))
- return;
- if (!entry->folded && !recursive)
- return;
- nr_entries = 1; /* for self */
- list_for_each_entry(child, &entry->children, node) {
- if (recursive)
- annotated_data_browser__unfold(browser, child, true);
- nr_entries += child->nr_entries;
- }
- entry->nr_entries = nr_entries;
- entry->folded = false;
- }
- static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser,
- bool recursive)
- {
- struct browser_entry *curr = browser->curr;
- struct browser_entry *parent;
- parent = curr->parent;
- while (parent) {
- parent->nr_entries -= curr->nr_entries;
- parent = parent->parent;
- }
- browser->b.nr_entries -= curr->nr_entries;
- if (curr->folded)
- annotated_data_browser__unfold(browser, curr, recursive);
- else
- annotated_data_browser__fold(browser, curr, recursive);
- parent = curr->parent;
- while (parent) {
- parent->nr_entries += curr->nr_entries;
- parent = parent->parent;
- }
- browser->b.nr_entries += curr->nr_entries;
- assert(browser->b.nr_entries == count_visible_entries(browser));
- }
- 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"
- "e Expand/Collapse current entry\n"
- "E Expand/Collapse all children of the current\n"
- "q/ESC/CTRL+C Exit\n\n");
- continue;
- case 'e':
- annotated_data_browser__toggle_fold(browser,
- /*recursive=*/false);
- break;
- case 'E':
- annotated_data_browser__toggle_fold(browser,
- /*recursive=*/true);
- break;
- 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 = browser__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)) {
- struct evsel *pos;
- int nr = 0;
- for_each_group_evsel(pos, evsel) {
- if (!symbol_conf.skip_empty ||
- evsel__hists(pos)->stats.nr_samples)
- nr++;
- }
- browser.nr_events = nr;
- }
- ret = annotated_data_browser__collect_entries(&browser);
- if (ret < 0)
- goto out;
- /* To get the top and current entry */
- browser__refresh(&browser.b);
- /* Show the first-level child entries by default */
- annotated_data_browser__toggle_fold(&browser, /*recursive=*/false);
- ret = annotated_data_browser__run(&browser, evsel, hbt);
- out:
- annotated_data_browser__delete_entries(&browser);
- return ret;
- }
|