Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615// 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: ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions"); 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;}