cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hists.c (17405B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include "../evlist.h"
      3#include "../callchain.h"
      4#include "../evsel.h"
      5#include "../sort.h"
      6#include "../hist.h"
      7#include "../helpline.h"
      8#include "../string2.h"
      9#include "gtk.h"
     10#include <signal.h>
     11#include <stdlib.h>
     12#include <linux/string.h>
     13
     14#define MAX_COLUMNS			32
     15
     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_FN
     79
     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 callchain
    130				 * 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 callchain
    151				 * 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 callchain
    252				 * 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		else
    262			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	else
    278		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	else
    290		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			else
    382				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			else
    444				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			else
    457				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		else
    661			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}