brintos

brintos / linux-shallow public Read only

0
0
Text · 17.0 KiB · bae21f3 Raw
682 lines · c
1// SPDX-License-Identifier: GPL-2.02#include "gtk.h"3#include "../evlist.h"4#include "../callchain.h"5#include "../evsel.h"6#include "../sort.h"7#include "../hist.h"8#include "../helpline.h"9#include "../string2.h"10#include <signal.h>11#include <stdlib.h>12#include <linux/string.h>13 14#define MAX_COLUMNS			3215 16static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)17{18	int ret = 0;19	int len;20	va_list args;21	double percent;22	const char *markup;23	char *buf = hpp->buf;24	size_t size = hpp->size;25 26	va_start(args, fmt);27	len = va_arg(args, int);28	percent = va_arg(args, double);29	va_end(args);30 31	markup = perf_gtk__get_percent_color(percent);32	if (markup)33		ret += scnprintf(buf, size, markup);34 35	ret += scnprintf(buf + ret, size - ret, fmt, len, percent);36 37	if (markup)38		ret += scnprintf(buf + ret, size - ret, "</span>");39 40	return ret;41}42 43#define __HPP_COLOR_PERCENT_FN(_type, _field)					\44static u64 he_get_##_field(struct hist_entry *he)				\45{										\46	return he->stat._field;							\47}										\48										\49static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\50				       struct perf_hpp *hpp,			\51				       struct hist_entry *he)			\52{										\53	return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%",		\54			__percent_color_snprintf, true);			\55}56 57#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)				\58static u64 he_get_acc_##_field(struct hist_entry *he)				\59{										\60	return he->stat_acc->_field;						\61}										\62										\63static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt,		\64				       struct perf_hpp *hpp,			\65				       struct hist_entry *he)			\66{										\67	return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", 	\68			    __percent_color_snprintf, true);			\69}70 71__HPP_COLOR_PERCENT_FN(overhead, period)72__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)73__HPP_COLOR_PERCENT_FN(overhead_us, period_us)74__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)75__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)76__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)77 78#undef __HPP_COLOR_PERCENT_FN79 80 81void perf_gtk__init_hpp(void)82{83	perf_hpp__format[PERF_HPP__OVERHEAD].color =84				perf_gtk__hpp_color_overhead;85	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =86				perf_gtk__hpp_color_overhead_sys;87	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =88				perf_gtk__hpp_color_overhead_us;89	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =90				perf_gtk__hpp_color_overhead_guest_sys;91	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =92				perf_gtk__hpp_color_overhead_guest_us;93	perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =94				perf_gtk__hpp_color_overhead_acc;95}96 97static void perf_gtk__add_callchain_flat(struct rb_root *root, GtkTreeStore *store,98					 GtkTreeIter *parent, int col, u64 total)99{100	struct rb_node *nd;101	bool has_single_node = (rb_first(root) == rb_last(root));102 103	for (nd = rb_first(root); nd; nd = rb_next(nd)) {104		struct callchain_node *node;105		struct callchain_list *chain;106		GtkTreeIter iter, new_parent;107		bool need_new_parent;108 109		node = rb_entry(nd, struct callchain_node, rb_node);110 111		new_parent = *parent;112		need_new_parent = !has_single_node;113 114		callchain_node__make_parent_list(node);115 116		list_for_each_entry(chain, &node->parent_val, list) {117			char buf[128];118 119			gtk_tree_store_append(store, &iter, &new_parent);120 121			callchain_node__scnprintf_value(node, buf, sizeof(buf), total);122			gtk_tree_store_set(store, &iter, 0, buf, -1);123 124			callchain_list__sym_name(chain, buf, sizeof(buf), false);125			gtk_tree_store_set(store, &iter, col, buf, -1);126 127			if (need_new_parent) {128				/*129				 * Only show the top-most symbol in a callchain130				 * if it's not the only callchain.131				 */132				new_parent = iter;133				need_new_parent = false;134			}135		}136 137		list_for_each_entry(chain, &node->val, list) {138			char buf[128];139 140			gtk_tree_store_append(store, &iter, &new_parent);141 142			callchain_node__scnprintf_value(node, buf, sizeof(buf), total);143			gtk_tree_store_set(store, &iter, 0, buf, -1);144 145			callchain_list__sym_name(chain, buf, sizeof(buf), false);146			gtk_tree_store_set(store, &iter, col, buf, -1);147 148			if (need_new_parent) {149				/*150				 * Only show the top-most symbol in a callchain151				 * if it's not the only callchain.152				 */153				new_parent = iter;154				need_new_parent = false;155			}156		}157	}158}159 160static void perf_gtk__add_callchain_folded(struct rb_root *root, GtkTreeStore *store,161					   GtkTreeIter *parent, int col, u64 total)162{163	struct rb_node *nd;164 165	for (nd = rb_first(root); nd; nd = rb_next(nd)) {166		struct callchain_node *node;167		struct callchain_list *chain;168		GtkTreeIter iter;169		char buf[64];170		char *str, *str_alloc = NULL;171		bool first = true;172 173		node = rb_entry(nd, struct callchain_node, rb_node);174 175		callchain_node__make_parent_list(node);176 177		list_for_each_entry(chain, &node->parent_val, list) {178			char name[1024];179 180			callchain_list__sym_name(chain, name, sizeof(name), false);181 182			if (asprintf(&str, "%s%s%s",183				     first ? "" : str_alloc,184				     first ? "" : symbol_conf.field_sep ?: "; ",185				     name) < 0)186				return;187 188			first = false;189			free(str_alloc);190			str_alloc = str;191		}192 193		list_for_each_entry(chain, &node->val, list) {194			char name[1024];195 196			callchain_list__sym_name(chain, name, sizeof(name), false);197 198			if (asprintf(&str, "%s%s%s",199				     first ? "" : str_alloc,200				     first ? "" : symbol_conf.field_sep ?: "; ",201				     name) < 0)202				return;203 204			first = false;205			free(str_alloc);206			str_alloc = str;207		}208 209		gtk_tree_store_append(store, &iter, parent);210 211		callchain_node__scnprintf_value(node, buf, sizeof(buf), total);212		gtk_tree_store_set(store, &iter, 0, buf, -1);213 214		gtk_tree_store_set(store, &iter, col, str, -1);215 216		free(str_alloc);217	}218}219 220static void perf_gtk__add_callchain_graph(struct rb_root *root, GtkTreeStore *store,221					  GtkTreeIter *parent, int col, u64 total)222{223	struct rb_node *nd;224	bool has_single_node = (rb_first(root) == rb_last(root));225 226	for (nd = rb_first(root); nd; nd = rb_next(nd)) {227		struct callchain_node *node;228		struct callchain_list *chain;229		GtkTreeIter iter, new_parent;230		bool need_new_parent;231		u64 child_total;232 233		node = rb_entry(nd, struct callchain_node, rb_node);234 235		new_parent = *parent;236		need_new_parent = !has_single_node && (node->val_nr > 1);237 238		list_for_each_entry(chain, &node->val, list) {239			char buf[128];240 241			gtk_tree_store_append(store, &iter, &new_parent);242 243			callchain_node__scnprintf_value(node, buf, sizeof(buf), total);244			gtk_tree_store_set(store, &iter, 0, buf, -1);245 246			callchain_list__sym_name(chain, buf, sizeof(buf), false);247			gtk_tree_store_set(store, &iter, col, buf, -1);248 249			if (need_new_parent) {250				/*251				 * Only show the top-most symbol in a callchain252				 * if it's not the only callchain.253				 */254				new_parent = iter;255				need_new_parent = false;256			}257		}258 259		if (callchain_param.mode == CHAIN_GRAPH_REL)260			child_total = node->children_hit;261		else262			child_total = total;263 264		/* Now 'iter' contains info of the last callchain_list */265		perf_gtk__add_callchain_graph(&node->rb_root, store, &iter, col,266					      child_total);267	}268}269 270static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,271				    GtkTreeIter *parent, int col, u64 total)272{273	if (callchain_param.mode == CHAIN_FLAT)274		perf_gtk__add_callchain_flat(root, store, parent, col, total);275	else if (callchain_param.mode == CHAIN_FOLDED)276		perf_gtk__add_callchain_folded(root, store, parent, col, total);277	else278		perf_gtk__add_callchain_graph(root, store, parent, col, total);279}280 281static void on_row_activated(GtkTreeView *view, GtkTreePath *path,282			     GtkTreeViewColumn *col __maybe_unused,283			     gpointer user_data __maybe_unused)284{285	bool expanded = gtk_tree_view_row_expanded(view, path);286 287	if (expanded)288		gtk_tree_view_collapse_row(view, path);289	else290		gtk_tree_view_expand_row(view, path, FALSE);291}292 293static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,294				 float min_pcnt)295{296	struct perf_hpp_fmt *fmt;297	GType col_types[MAX_COLUMNS];298	GtkCellRenderer *renderer;299	GtkTreeStore *store;300	struct rb_node *nd;301	GtkWidget *view;302	int col_idx;303	int sym_col = -1;304	int nr_cols;305	char s[512];306 307	struct perf_hpp hpp = {308		.buf		= s,309		.size		= sizeof(s),310	};311 312	nr_cols = 0;313 314	hists__for_each_format(hists, fmt)315		col_types[nr_cols++] = G_TYPE_STRING;316 317	store = gtk_tree_store_newv(nr_cols, col_types);318 319	view = gtk_tree_view_new();320 321	renderer = gtk_cell_renderer_text_new();322 323	col_idx = 0;324 325	hists__for_each_format(hists, fmt) {326		if (perf_hpp__should_skip(fmt, hists))327			continue;328 329		/*330		 * XXX no way to determine where symcol column is..331		 *     Just use last column for now.332		 */333		if (perf_hpp__is_sort_entry(fmt))334			sym_col = col_idx;335 336		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),337							    -1, fmt->name,338							    renderer, "markup",339							    col_idx++, NULL);340	}341 342	for (col_idx = 0; col_idx < nr_cols; col_idx++) {343		GtkTreeViewColumn *column;344 345		column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx);346		gtk_tree_view_column_set_resizable(column, TRUE);347 348		if (col_idx == sym_col) {349			gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view),350							  column);351		}352	}353 354	gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));355 356	g_object_unref(GTK_TREE_MODEL(store));357 358	for (nd = rb_first_cached(&hists->entries); nd; nd = rb_next(nd)) {359		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);360		GtkTreeIter iter;361		u64 total = hists__total_period(h->hists);362		float percent;363 364		if (h->filtered)365			continue;366 367		percent = hist_entry__get_percent_limit(h);368		if (percent < min_pcnt)369			continue;370 371		gtk_tree_store_append(store, &iter, NULL);372 373		col_idx = 0;374 375		hists__for_each_format(hists, fmt) {376			if (perf_hpp__should_skip(fmt, h->hists))377				continue;378 379			if (fmt->color)380				fmt->color(fmt, &hpp, h);381			else382				fmt->entry(fmt, &hpp, h);383 384			gtk_tree_store_set(store, &iter, col_idx++, s, -1);385		}386 387		if (hist_entry__has_callchains(h) &&388		    symbol_conf.use_callchain && hists__has(hists, sym)) {389			if (callchain_param.mode == CHAIN_GRAPH_REL)390				total = symbol_conf.cumulate_callchain ?391					h->stat_acc->period : h->stat.period;392 393			perf_gtk__add_callchain(&h->sorted_chain, store, &iter,394						sym_col, total);395		}396	}397 398	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);399 400	g_signal_connect(view, "row-activated",401			 G_CALLBACK(on_row_activated), NULL);402	gtk_container_add(GTK_CONTAINER(window), view);403}404 405static void perf_gtk__add_hierarchy_entries(struct hists *hists,406					    struct rb_root_cached *root,407					    GtkTreeStore *store,408					    GtkTreeIter *parent,409					    struct perf_hpp *hpp,410					    float min_pcnt)411{412	int col_idx = 0;413	struct rb_node *node;414	struct hist_entry *he;415	struct perf_hpp_fmt *fmt;416	struct perf_hpp_list_node *fmt_node;417	u64 total = hists__total_period(hists);418	int size;419 420	for (node = rb_first_cached(root); node; node = rb_next(node)) {421		GtkTreeIter iter;422		float percent;423		char *bf;424 425		he = rb_entry(node, struct hist_entry, rb_node);426		if (he->filtered)427			continue;428 429		percent = hist_entry__get_percent_limit(he);430		if (percent < min_pcnt)431			continue;432 433		gtk_tree_store_append(store, &iter, parent);434 435		col_idx = 0;436 437		/* the first hpp_list_node is for overhead columns */438		fmt_node = list_first_entry(&hists->hpp_formats,439					    struct perf_hpp_list_node, list);440		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {441			if (fmt->color)442				fmt->color(fmt, hpp, he);443			else444				fmt->entry(fmt, hpp, he);445 446			gtk_tree_store_set(store, &iter, col_idx++, hpp->buf, -1);447		}448 449		bf = hpp->buf;450		size = hpp->size;451		perf_hpp_list__for_each_format(he->hpp_list, fmt) {452			int ret;453 454			if (fmt->color)455				ret = fmt->color(fmt, hpp, he);456			else457				ret = fmt->entry(fmt, hpp, he);458 459			snprintf(hpp->buf + ret, hpp->size - ret, "  ");460			advance_hpp(hpp, ret + 2);461		}462 463		gtk_tree_store_set(store, &iter, col_idx, strim(bf), -1);464 465		if (!he->leaf) {466			hpp->buf = bf;467			hpp->size = size;468 469			perf_gtk__add_hierarchy_entries(hists, &he->hroot_out,470							store, &iter, hpp,471							min_pcnt);472 473			if (!hist_entry__has_hierarchy_children(he, min_pcnt)) {474				char buf[32];475				GtkTreeIter child;476 477				snprintf(buf, sizeof(buf), "no entry >= %.2f%%",478					 min_pcnt);479 480				gtk_tree_store_append(store, &child, &iter);481				gtk_tree_store_set(store, &child, col_idx, buf, -1);482			}483		}484 485		if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) {486			if (callchain_param.mode == CHAIN_GRAPH_REL)487				total = symbol_conf.cumulate_callchain ?488					he->stat_acc->period : he->stat.period;489 490			perf_gtk__add_callchain(&he->sorted_chain, store, &iter,491						col_idx, total);492		}493	}494 495}496 497static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,498				     float min_pcnt)499{500	struct perf_hpp_fmt *fmt;501	struct perf_hpp_list_node *fmt_node;502	GType col_types[MAX_COLUMNS];503	GtkCellRenderer *renderer;504	GtkTreeStore *store;505	GtkWidget *view;506	int col_idx;507	int nr_cols = 0;508	char s[512];509	char buf[512];510	bool first_node, first_col;511	struct perf_hpp hpp = {512		.buf		= s,513		.size		= sizeof(s),514	};515 516	hists__for_each_format(hists, fmt) {517		if (perf_hpp__is_sort_entry(fmt) ||518		    perf_hpp__is_dynamic_entry(fmt))519			break;520 521		col_types[nr_cols++] = G_TYPE_STRING;522	}523	col_types[nr_cols++] = G_TYPE_STRING;524 525	store = gtk_tree_store_newv(nr_cols, col_types);526	view = gtk_tree_view_new();527	renderer = gtk_cell_renderer_text_new();528 529	col_idx = 0;530 531	/* the first hpp_list_node is for overhead columns */532	fmt_node = list_first_entry(&hists->hpp_formats,533				    struct perf_hpp_list_node, list);534	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {535		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),536							    -1, fmt->name,537							    renderer, "markup",538							    col_idx++, NULL);539	}540 541	/* construct merged column header since sort keys share single column */542	buf[0] = '\0';543	first_node = true;544	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {545		if (!first_node)546			strcat(buf, " / ");547		first_node = false;548 549		first_col = true;550		perf_hpp_list__for_each_format(&fmt_node->hpp ,fmt) {551			if (perf_hpp__should_skip(fmt, hists))552				continue;553 554			if (!first_col)555				strcat(buf, "+");556			first_col = false;557 558			fmt->header(fmt, &hpp, hists, 0, NULL);559			strcat(buf, strim(hpp.buf));560		}561	}562 563	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),564						    -1, buf,565						    renderer, "markup",566						    col_idx++, NULL);567 568	for (col_idx = 0; col_idx < nr_cols; col_idx++) {569		GtkTreeViewColumn *column;570 571		column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx);572		gtk_tree_view_column_set_resizable(column, TRUE);573 574		if (col_idx == 0) {575			gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view),576							  column);577		}578	}579 580	gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));581	g_object_unref(GTK_TREE_MODEL(store));582 583	perf_gtk__add_hierarchy_entries(hists, &hists->entries, store,584					NULL, &hpp, min_pcnt);585 586	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);587 588	g_signal_connect(view, "row-activated",589			 G_CALLBACK(on_row_activated), NULL);590	gtk_container_add(GTK_CONTAINER(window), view);591}592 593int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,594			     struct hist_browser_timer *hbt __maybe_unused, float min_pcnt)595{596	struct evsel *pos;597	GtkWidget *vbox;598	GtkWidget *notebook;599	GtkWidget *info_bar;600	GtkWidget *statbar;601	GtkWidget *window;602 603	signal(SIGSEGV, perf_gtk__signal);604	signal(SIGFPE,  perf_gtk__signal);605	signal(SIGINT,  perf_gtk__signal);606	signal(SIGQUIT, perf_gtk__signal);607	signal(SIGTERM, perf_gtk__signal);608 609	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);610 611	gtk_window_set_title(GTK_WINDOW(window), "perf report");612 613	g_signal_connect(window, "delete_event", gtk_main_quit, NULL);614 615	pgctx = perf_gtk__activate_context(window);616	if (!pgctx)617		return -1;618 619	vbox = gtk_vbox_new(FALSE, 0);620 621	notebook = gtk_notebook_new();622 623	gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);624 625	info_bar = perf_gtk__setup_info_bar();626	if (info_bar)627		gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);628 629	statbar = perf_gtk__setup_statusbar();630	gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);631 632	gtk_container_add(GTK_CONTAINER(window), vbox);633 634	evlist__for_each_entry(evlist, pos) {635		struct hists *hists = evsel__hists(pos);636		const char *evname = evsel__name(pos);637		GtkWidget *scrolled_window;638		GtkWidget *tab_label;639		char buf[512];640		size_t size = sizeof(buf);641 642		if (symbol_conf.event_group) {643			if (!evsel__is_group_leader(pos))644				continue;645 646			if (pos->core.nr_members > 1) {647				evsel__group_desc(pos, buf, size);648				evname = buf;649			}650		}651 652		scrolled_window = gtk_scrolled_window_new(NULL, NULL);653 654		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),655							GTK_POLICY_AUTOMATIC,656							GTK_POLICY_AUTOMATIC);657 658		if (symbol_conf.report_hierarchy)659			perf_gtk__show_hierarchy(scrolled_window, hists, min_pcnt);660		else661			perf_gtk__show_hists(scrolled_window, hists, min_pcnt);662 663		tab_label = gtk_label_new(evname);664 665		gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);666	}667 668	gtk_widget_show_all(window);669 670	perf_gtk__resize_window(window);671 672	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);673 674	ui_helpline__push(help);675 676	gtk_main();677 678	perf_gtk__deactivate_context(&pgctx);679 680	return 0;681}682