brintos

brintos / linux-shallow public Read only

0
0
Text · 14.9 KiB · cd562a8 Raw
614 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <inttypes.h>3#include <string.h>4#include <linux/zalloc.h>5#include <sys/ttydefaults.h>6 7#include "ui/browser.h"8#include "ui/helpline.h"9#include "ui/keysyms.h"10#include "ui/ui.h"11#include "util/annotate.h"12#include "util/annotate-data.h"13#include "util/evsel.h"14#include "util/evlist.h"15#include "util/sort.h"16 17#define FOLDED_SIGN  '+'18#define UNFOLD_SIGN  '-'19#define NOCHLD_SIGN  ' '20 21struct browser_entry {22	struct list_head node;23	struct annotated_member *data;24	struct type_hist_entry *hists;25	struct browser_entry *parent;26	struct list_head children;27	int indent;  /*indentation level, starts from 0 */28	int nr_entries; /* # of visible entries: self + descendents */29	bool folded;  /* only can be false when it has children */30};31 32struct annotated_data_browser {33	struct ui_browser b;34	struct list_head entries;35	struct browser_entry *curr;36	int nr_events;37};38 39static struct annotated_data_browser *get_browser(struct ui_browser *uib)40{41	return container_of(uib, struct annotated_data_browser, b);42}43 44static void update_hist_entry(struct type_hist_entry *dst,45			      struct type_hist_entry *src)46{47	dst->nr_samples += src->nr_samples;48	dst->period += src->period;49}50 51static int get_member_overhead(struct annotated_data_type *adt,52			       struct browser_entry *entry,53			       struct evsel *leader)54{55	struct annotated_member *member = entry->data;56	int i, k;57 58	for (i = 0; i < member->size; i++) {59		struct type_hist *h;60		struct evsel *evsel;61		int offset = member->offset + i;62 63		k = 0;64		for_each_group_evsel(evsel, leader) {65			if (symbol_conf.skip_empty &&66			    evsel__hists(evsel)->stats.nr_samples == 0)67				continue;68 69			h = adt->histograms[evsel->core.idx];70			update_hist_entry(&entry->hists[k++], &h->addr[offset]);71		}72	}73	return 0;74}75 76static int add_child_entries(struct annotated_data_browser *browser,77			     struct browser_entry *parent,78			     struct annotated_data_type *adt,79			     struct annotated_member *member,80			     struct evsel *evsel, int indent)81{82	struct annotated_member *pos;83	struct browser_entry *entry;84	struct list_head *parent_list;85 86	entry = zalloc(sizeof(*entry));87	if (entry == NULL)88		return -1;89 90	entry->hists = calloc(browser->nr_events, sizeof(*entry->hists));91	if (entry->hists == NULL) {92		free(entry);93		return -1;94	}95 96	entry->data = member;97	entry->parent = parent;98	entry->indent = indent;99	if (get_member_overhead(adt, entry, evsel) < 0) {100		free(entry);101		return -1;102	}103 104	INIT_LIST_HEAD(&entry->children);105	if (parent)106		parent_list = &parent->children;107	else108		parent_list = &browser->entries;109 110	list_add_tail(&entry->node, parent_list);111 112	list_for_each_entry(pos, &member->children, node) {113		int nr = add_child_entries(browser, entry, adt, pos, evsel,114					   indent + 1);115		if (nr < 0)116			return nr;117	}118 119	/* add an entry for the closing bracket ("}") */120	if (!list_empty(&member->children)) {121		struct browser_entry *bracket;122 123		bracket = zalloc(sizeof(*bracket));124		if (bracket == NULL)125			return -1;126 127		bracket->indent = indent;128		bracket->parent = entry;129		bracket->folded = true;130		bracket->nr_entries = 1;131 132		INIT_LIST_HEAD(&bracket->children);133		list_add_tail(&bracket->node, &entry->children);134	}135 136	/* fold child entries by default */137	entry->folded = true;138	entry->nr_entries = 1;139	return 0;140}141 142static u32 count_visible_entries(struct annotated_data_browser *browser)143{144	int nr = 0;145	struct browser_entry *entry;146 147	list_for_each_entry(entry, &browser->entries, node)148		nr += entry->nr_entries;149 150	return nr;151}152 153static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser)154{155	struct hist_entry *he = browser->b.priv;156	struct annotated_data_type *adt = he->mem_type;157	struct evsel *evsel = hists_to_evsel(he->hists);158 159	INIT_LIST_HEAD(&browser->entries);160 161	add_child_entries(browser, /*parent=*/NULL, adt, &adt->self, evsel,162			  /*indent=*/0);163 164	browser->b.entries = &browser->entries;165	browser->b.nr_entries = count_visible_entries(browser);166	return 0;167}168 169static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser)170{171	struct browser_entry *pos, *tmp;172 173	list_for_each_entry_safe(pos, tmp, &browser->entries, node) {174		list_del_init(&pos->node);175		zfree(&pos->hists);176		free(pos);177	}178}179 180static struct browser_entry *get_first_child(struct browser_entry *entry)181{182	if (list_empty(&entry->children))183		return NULL;184 185	return list_first_entry(&entry->children, struct browser_entry, node);186}187 188static struct browser_entry *get_last_child(struct browser_entry *entry)189{190	if (list_empty(&entry->children))191		return NULL;192 193	return list_last_entry(&entry->children, struct browser_entry, node);194}195 196static bool is_first_child(struct browser_entry *entry)197{198	/* This will be checked in a different way */199	if (entry->parent == NULL)200		return false;201 202	return get_first_child(entry->parent) == entry;203}204 205static bool is_last_child(struct browser_entry *entry)206{207	/* This will be checked in a different way */208	if (entry->parent == NULL)209		return false;210 211	return get_last_child(entry->parent) == entry;212}213 214static struct browser_entry *browser__prev_entry(struct ui_browser *uib,215						 struct browser_entry *entry)216{217	struct annotated_data_browser *browser = get_browser(uib);218	struct browser_entry *first;219 220	first = list_first_entry(&browser->entries, struct browser_entry, node);221 222	while (entry != first) {223		if (is_first_child(entry))224			entry = entry->parent;225		else {226			entry = list_prev_entry(entry, node);227			while (!entry->folded)228				entry = get_last_child(entry);229		}230 231		if (!uib->filter || !uib->filter(uib, &entry->node))232			return entry;233	}234	return first;235}236 237static struct browser_entry *browser__next_entry(struct ui_browser *uib,238						 struct browser_entry *entry)239{240	struct annotated_data_browser *browser = get_browser(uib);241	struct browser_entry *last;242 243	last = list_last_entry(&browser->entries, struct browser_entry, node);244	while (!last->folded)245		last = get_last_child(last);246 247	while (entry != last) {248		if (!entry->folded)249			entry = get_first_child(entry);250		else {251			while (is_last_child(entry))252				entry = entry->parent;253 254			entry = list_next_entry(entry, node);255		}256 257		if (!uib->filter || !uib->filter(uib, &entry->node))258			return entry;259	}260	return last;261}262 263static void browser__seek(struct ui_browser *uib, off_t offset, int whence)264{265	struct annotated_data_browser *browser = get_browser(uib);266	struct browser_entry *entry;267 268	if (uib->nr_entries == 0)269		return;270 271	switch (whence) {272	case SEEK_SET:273		entry = list_first_entry(&browser->entries, typeof(*entry), node);274		if (uib->filter && uib->filter(uib, &entry->node))275			entry = browser__next_entry(uib, entry);276		break;277	case SEEK_CUR:278		entry = list_entry(uib->top, typeof(*entry), node);279		break;280	case SEEK_END:281		entry = list_last_entry(&browser->entries, typeof(*entry), node);282		while (!entry->folded)283			entry = get_last_child(entry);284		if (uib->filter && uib->filter(uib, &entry->node))285			entry = browser__prev_entry(uib, entry);286		break;287	default:288		return;289	}290 291	assert(entry != NULL);292 293	if (offset > 0) {294		while (offset-- != 0)295			entry = browser__next_entry(uib, entry);296	} else {297		while (offset++ != 0)298			entry = browser__prev_entry(uib, entry);299	}300 301	uib->top = &entry->node;302}303 304static unsigned int browser__refresh(struct ui_browser *uib)305{306	struct annotated_data_browser *browser = get_browser(uib);307	struct browser_entry *entry, *next;308	int row = 0;309 310	if (uib->top == NULL || uib->top == uib->entries)311		browser__seek(uib, SEEK_SET, 0);312 313	entry = list_entry(uib->top, typeof(*entry), node);314 315	while (true) {316		if (!uib->filter || !uib->filter(uib, &entry->node)) {317			ui_browser__gotorc(uib, row, 0);318			uib->write(uib, entry, row);319			if (uib->top_idx + row == uib->index)320				browser->curr = entry;321			if (++row == uib->rows)322				break;323		}324		next = browser__next_entry(uib, entry);325		if (next == entry)326			break;327 328		entry = next;329	}330 331	return row;332}333 334static int browser__show(struct ui_browser *uib)335{336	struct hist_entry *he = uib->priv;337	struct annotated_data_type *adt = he->mem_type;338	struct annotated_data_browser *browser = get_browser(uib);339	const char *help = "Press 'h' for help on key bindings";340	char title[256];341 342	snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)",343		 adt->self.type_name, he->stat.nr_events);344 345	if (ui_browser__show(uib, title, help) < 0)346		return -1;347 348	/* second line header */349	ui_browser__gotorc_title(uib, 0, 0);350	ui_browser__set_color(uib, HE_COLORSET_ROOT);351 352	if (symbol_conf.show_total_period)353		strcpy(title, "Period");354	else if (symbol_conf.show_nr_samples)355		strcpy(title, "Samples");356	else357		strcpy(title, "Percent");358 359	ui_browser__printf(uib, "%*s %10s %10s %10s  %s",360			   2 + 11 * (browser->nr_events - 1), "",361			   title, "Offset", "Size", "Field");362	ui_browser__write_nstring(uib, "", uib->width);363	return 0;364}365 366static void browser__write_overhead(struct ui_browser *uib,367				    struct type_hist *total,368				    struct type_hist_entry *hist, int row)369{370	u64 period = hist->period;371	double percent = total->period ? (100.0 * period / total->period) : 0;372	bool current = ui_browser__is_current_entry(uib, row);373	int nr_samples = 0;374 375	ui_browser__set_percent_color(uib, percent, current);376 377	if (symbol_conf.show_total_period)378		ui_browser__printf(uib, " %10" PRIu64, period);379	else if (symbol_conf.show_nr_samples)380		ui_browser__printf(uib, " %10d", nr_samples);381	else382		ui_browser__printf(uib, " %10.2f", percent);383 384	ui_browser__set_percent_color(uib, 0, current);385}386 387static void browser__write(struct ui_browser *uib, void *entry, int row)388{389	struct annotated_data_browser *browser = get_browser(uib);390	struct browser_entry *be = entry;391	struct annotated_member *member = be->data;392	struct hist_entry *he = uib->priv;393	struct annotated_data_type *adt = he->mem_type;394	struct evsel *leader = hists_to_evsel(he->hists);395	struct evsel *evsel;396	int idx = 0;397	bool current = ui_browser__is_current_entry(uib, row);398 399	if (member == NULL) {400		/* print the closing bracket */401		ui_browser__set_percent_color(uib, 0, current);402		ui_browser__printf(uib, "%c ", NOCHLD_SIGN);403		ui_browser__write_nstring(uib, "", 11 * browser->nr_events);404		ui_browser__printf(uib, " %10s %10s  %*s};",405				   "", "", be->indent * 4, "");406		ui_browser__write_nstring(uib, "", uib->width);407		return;408	}409 410	ui_browser__set_percent_color(uib, 0, current);411 412	if (!list_empty(&be->children))413		ui_browser__printf(uib, "%c ", be->folded ? FOLDED_SIGN : UNFOLD_SIGN);414	else415		ui_browser__printf(uib, "%c ", NOCHLD_SIGN);416 417	/* print the number */418	for_each_group_evsel(evsel, leader) {419		struct type_hist *h = adt->histograms[evsel->core.idx];420 421		if (symbol_conf.skip_empty &&422		    evsel__hists(evsel)->stats.nr_samples == 0)423			continue;424 425		browser__write_overhead(uib, h, &be->hists[idx++], row);426	}427 428	/* print type info */429	if (be->indent == 0 && !member->var_name) {430		ui_browser__printf(uib, " %#10x %#10x  %s%s",431				   member->offset, member->size,432				   member->type_name,433				   list_empty(&member->children) || be->folded? ";" : " {");434	} else {435		ui_browser__printf(uib, " %#10x %#10x  %*s%s\t%s%s",436				   member->offset, member->size,437				   be->indent * 4, "", member->type_name,438				   member->var_name ?: "",439				   list_empty(&member->children) || be->folded ? ";" : " {");440	}441	/* fill the rest */442	ui_browser__write_nstring(uib, "", uib->width);443}444 445static void annotated_data_browser__fold(struct annotated_data_browser *browser,446					 struct browser_entry *entry,447					 bool recursive)448{449	struct browser_entry *child;450 451	if (list_empty(&entry->children))452		return;453	if (entry->folded && !recursive)454		return;455 456	if (recursive) {457		list_for_each_entry(child, &entry->children, node)458			annotated_data_browser__fold(browser, child, true);459	}460 461	entry->nr_entries = 1;462	entry->folded = true;463}464 465static void annotated_data_browser__unfold(struct annotated_data_browser *browser,466					   struct browser_entry *entry,467					   bool recursive)468{469	struct browser_entry *child;470	int nr_entries;471 472	if (list_empty(&entry->children))473		return;474	if (!entry->folded && !recursive)475		return;476 477	nr_entries = 1; /* for self */478	list_for_each_entry(child, &entry->children, node) {479		if (recursive)480			annotated_data_browser__unfold(browser, child, true);481 482		nr_entries += child->nr_entries;483	}484 485	entry->nr_entries = nr_entries;486	entry->folded = false;487}488 489static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser,490						bool recursive)491{492	struct browser_entry *curr = browser->curr;493	struct browser_entry *parent;494 495	parent = curr->parent;496	while (parent) {497		parent->nr_entries -= curr->nr_entries;498		parent = parent->parent;499	}500	browser->b.nr_entries -= curr->nr_entries;501 502	if (curr->folded)503		annotated_data_browser__unfold(browser, curr, recursive);504	else505		annotated_data_browser__fold(browser, curr, recursive);506 507	parent = curr->parent;508	while (parent) {509		parent->nr_entries += curr->nr_entries;510		parent = parent->parent;511	}512	browser->b.nr_entries += curr->nr_entries;513 514	assert(browser->b.nr_entries == count_visible_entries(browser));515}516 517static int annotated_data_browser__run(struct annotated_data_browser *browser,518				       struct evsel *evsel __maybe_unused,519				       struct hist_browser_timer *hbt)520{521	int delay_secs = hbt ? hbt->refresh : 0;522	int key;523 524	if (browser__show(&browser->b) < 0)525		return -1;526 527	while (1) {528		key = ui_browser__run(&browser->b, delay_secs);529 530		switch (key) {531		case K_TIMER:532			if (hbt)533				hbt->timer(hbt->arg);534			continue;535		case K_F1:536		case 'h':537			ui_browser__help_window(&browser->b,538		"UP/DOWN/PGUP\n"539		"PGDN/SPACE    Navigate\n"540		"</>           Move to prev/next symbol\n"541		"e             Expand/Collapse current entry\n"542		"E             Expand/Collapse all children of the current\n"543		"q/ESC/CTRL+C  Exit\n\n");544			continue;545		case 'e':546			annotated_data_browser__toggle_fold(browser,547							    /*recursive=*/false);548			break;549		case 'E':550			annotated_data_browser__toggle_fold(browser,551							    /*recursive=*/true);552			break;553		case K_LEFT:554		case '<':555		case '>':556		case K_ESC:557		case 'q':558		case CTRL('c'):559			goto out;560		default:561			continue;562		}563	}564out:565	ui_browser__hide(&browser->b);566	return key;567}568 569int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel,570				  struct hist_browser_timer *hbt)571{572	struct annotated_data_browser browser = {573		.b = {574			.refresh = browser__refresh,575			.seek	 = browser__seek,576			.write	 = browser__write,577			.priv	 = he,578			.extra_title_lines = 1,579		},580		.nr_events = 1,581	};582	int ret;583 584	ui_helpline__push("Press ESC to exit");585 586	if (evsel__is_group_event(evsel)) {587		struct evsel *pos;588		int nr = 0;589 590		for_each_group_evsel(pos, evsel) {591			if (!symbol_conf.skip_empty ||592			    evsel__hists(pos)->stats.nr_samples)593				nr++;594		}595		browser.nr_events = nr;596	}597 598	ret = annotated_data_browser__collect_entries(&browser);599	if (ret < 0)600		goto out;601 602	/* To get the top and current entry */603	browser__refresh(&browser.b);604	/* Show the first-level child entries by default */605	annotated_data_browser__toggle_fold(&browser, /*recursive=*/false);606 607	ret = annotated_data_browser__run(&browser, evsel, hbt);608 609out:610	annotated_data_browser__delete_entries(&browser);611 612	return ret;613}614