LingmoOS-kernel/tools/perf/ui/browsers/annotate-data.c

314 lines
7.7 KiB
C

// 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"
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;
}