diff options
Diffstat (limited to 'tools/perf/ui/browsers/annotate-data.c')
| -rw-r--r-- | tools/perf/ui/browsers/annotate-data.c | 313 | 
1 files changed, 313 insertions, 0 deletions
diff --git a/tools/perf/ui/browsers/annotate-data.c b/tools/perf/ui/browsers/annotate-data.c new file mode 100644 index 000000000000..8d6bf08d371d --- /dev/null +++ b/tools/perf/ui/browsers/annotate-data.c @@ -0,0 +1,313 @@ +// 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; +}  |