Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1#include "../evlist.h"
  2#include "../cache.h"
  3#include "../evsel.h"
  4#include "../sort.h"
  5#include "../hist.h"
  6#include "../helpline.h"
  7#include "gtk.h"
  8
  9#define MAX_COLUMNS			32
 10
 11static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...)
 12{
 13	int ret = 0;
 14	va_list args;
 15	double percent;
 16	const char *markup;
 17	char *buf = hpp->buf;
 18	size_t size = hpp->size;
 19
 20	va_start(args, fmt);
 21	percent = va_arg(args, double);
 22	va_end(args);
 23
 24	markup = perf_gtk__get_percent_color(percent);
 25	if (markup)
 26		ret += scnprintf(buf, size, markup);
 27
 28	ret += scnprintf(buf + ret, size - ret, fmt, percent);
 29
 30	if (markup)
 31		ret += scnprintf(buf + ret, size - ret, "</span>");
 32
 33	return ret;
 34}
 35
 36#define __HPP_COLOR_PERCENT_FN(_type, _field)					\
 37static u64 he_get_##_field(struct hist_entry *he)				\
 38{										\
 39	return he->stat._field;							\
 40}										\
 41										\
 42static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,	\
 43				       struct perf_hpp *hpp,			\
 44				       struct hist_entry *he)			\
 45{										\
 46	return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%",		\
 47			  __percent_color_snprintf, true);			\
 48}
 49
 50__HPP_COLOR_PERCENT_FN(overhead, period)
 51__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
 52__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
 53__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
 54__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
 55
 56#undef __HPP_COLOR_PERCENT_FN
 57
 58
 59void perf_gtk__init_hpp(void)
 60{
 61	perf_hpp__init();
 62
 63	perf_hpp__format[PERF_HPP__OVERHEAD].color =
 64				perf_gtk__hpp_color_overhead;
 65	perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
 66				perf_gtk__hpp_color_overhead_sys;
 67	perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
 68				perf_gtk__hpp_color_overhead_us;
 69	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
 70				perf_gtk__hpp_color_overhead_guest_sys;
 71	perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
 72				perf_gtk__hpp_color_overhead_guest_us;
 73}
 74
 75static void callchain_list__sym_name(struct callchain_list *cl,
 76				     char *bf, size_t bfsize)
 77{
 78	if (cl->ms.sym)
 79		scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
 80	else
 81		scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
 82}
 83
 84static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
 85				    GtkTreeIter *parent, int col, u64 total)
 86{
 87	struct rb_node *nd;
 88	bool has_single_node = (rb_first(root) == rb_last(root));
 89
 90	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
 91		struct callchain_node *node;
 92		struct callchain_list *chain;
 93		GtkTreeIter iter, new_parent;
 94		bool need_new_parent;
 95		double percent;
 96		u64 hits, child_total;
 97
 98		node = rb_entry(nd, struct callchain_node, rb_node);
 99
100		hits = callchain_cumul_hits(node);
101		percent = 100.0 * hits / total;
102
103		new_parent = *parent;
104		need_new_parent = !has_single_node && (node->val_nr > 1);
105
106		list_for_each_entry(chain, &node->val, list) {
107			char buf[128];
108
109			gtk_tree_store_append(store, &iter, &new_parent);
110
111			scnprintf(buf, sizeof(buf), "%5.2f%%", percent);
112			gtk_tree_store_set(store, &iter, 0, buf, -1);
113
114			callchain_list__sym_name(chain, buf, sizeof(buf));
115			gtk_tree_store_set(store, &iter, col, buf, -1);
116
117			if (need_new_parent) {
118				/*
119				 * Only show the top-most symbol in a callchain
120				 * if it's not the only callchain.
121				 */
122				new_parent = iter;
123				need_new_parent = false;
124			}
125		}
126
127		if (callchain_param.mode == CHAIN_GRAPH_REL)
128			child_total = node->children_hit;
129		else
130			child_total = total;
131
132		/* Now 'iter' contains info of the last callchain_list */
133		perf_gtk__add_callchain(&node->rb_root, store, &iter, col,
134					child_total);
135	}
136}
137
138static void on_row_activated(GtkTreeView *view, GtkTreePath *path,
139			     GtkTreeViewColumn *col __maybe_unused,
140			     gpointer user_data __maybe_unused)
141{
142	bool expanded = gtk_tree_view_row_expanded(view, path);
143
144	if (expanded)
145		gtk_tree_view_collapse_row(view, path);
146	else
147		gtk_tree_view_expand_row(view, path, FALSE);
148}
149
150static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
151				 float min_pcnt)
152{
153	struct perf_hpp_fmt *fmt;
154	GType col_types[MAX_COLUMNS];
155	GtkCellRenderer *renderer;
156	struct sort_entry *se;
157	GtkTreeStore *store;
158	struct rb_node *nd;
159	GtkWidget *view;
160	int col_idx;
161	int sym_col = -1;
162	int nr_cols;
163	char s[512];
164
165	struct perf_hpp hpp = {
166		.buf		= s,
167		.size		= sizeof(s),
168	};
169
170	nr_cols = 0;
171
172	perf_hpp__for_each_format(fmt)
173		col_types[nr_cols++] = G_TYPE_STRING;
174
175	list_for_each_entry(se, &hist_entry__sort_list, list) {
176		if (se->elide)
177			continue;
178
179		if (se == &sort_sym)
180			sym_col = nr_cols;
181
182		col_types[nr_cols++] = G_TYPE_STRING;
183	}
184
185	store = gtk_tree_store_newv(nr_cols, col_types);
186
187	view = gtk_tree_view_new();
188
189	renderer = gtk_cell_renderer_text_new();
190
191	col_idx = 0;
192
193	perf_hpp__for_each_format(fmt) {
194		fmt->header(fmt, &hpp, hists_to_evsel(hists));
195
196		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
197							    -1, ltrim(s),
198							    renderer, "markup",
199							    col_idx++, NULL);
200	}
201
202	list_for_each_entry(se, &hist_entry__sort_list, list) {
203		if (se->elide)
204			continue;
205
206		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
207							    -1, se->se_header,
208							    renderer, "text",
209							    col_idx++, NULL);
210	}
211
212	for (col_idx = 0; col_idx < nr_cols; col_idx++) {
213		GtkTreeViewColumn *column;
214
215		column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx);
216		gtk_tree_view_column_set_resizable(column, TRUE);
217
218		if (col_idx == sym_col) {
219			gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view),
220							  column);
221		}
222	}
223
224	gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
225
226	g_object_unref(GTK_TREE_MODEL(store));
227
228	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
229		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
230		GtkTreeIter iter;
231		float percent = h->stat.period * 100.0 /
232					hists->stats.total_period;
233
234		if (h->filtered)
235			continue;
236
237		if (percent < min_pcnt)
238			continue;
239
240		gtk_tree_store_append(store, &iter, NULL);
241
242		col_idx = 0;
243
244		perf_hpp__for_each_format(fmt) {
245			if (fmt->color)
246				fmt->color(fmt, &hpp, h);
247			else
248				fmt->entry(fmt, &hpp, h);
249
250			gtk_tree_store_set(store, &iter, col_idx++, s, -1);
251		}
252
253		list_for_each_entry(se, &hist_entry__sort_list, list) {
254			if (se->elide)
255				continue;
256
257			se->se_snprintf(h, s, ARRAY_SIZE(s),
258					hists__col_len(hists, se->se_width_idx));
259
260			gtk_tree_store_set(store, &iter, col_idx++, s, -1);
261		}
262
263		if (symbol_conf.use_callchain && sort__has_sym) {
264			u64 total;
265
266			if (callchain_param.mode == CHAIN_GRAPH_REL)
267				total = h->stat.period;
268			else
269				total = hists->stats.total_period;
270
271			perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
272						sym_col, total);
273		}
274	}
275
276	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
277
278	g_signal_connect(view, "row-activated",
279			 G_CALLBACK(on_row_activated), NULL);
280	gtk_container_add(GTK_CONTAINER(window), view);
281}
282
283int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
284				  const char *help,
285				  struct hist_browser_timer *hbt __maybe_unused,
286				  float min_pcnt)
287{
288	struct perf_evsel *pos;
289	GtkWidget *vbox;
290	GtkWidget *notebook;
291	GtkWidget *info_bar;
292	GtkWidget *statbar;
293	GtkWidget *window;
294
295	signal(SIGSEGV, perf_gtk__signal);
296	signal(SIGFPE,  perf_gtk__signal);
297	signal(SIGINT,  perf_gtk__signal);
298	signal(SIGQUIT, perf_gtk__signal);
299	signal(SIGTERM, perf_gtk__signal);
300
301	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
302
303	gtk_window_set_title(GTK_WINDOW(window), "perf report");
304
305	g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
306
307	pgctx = perf_gtk__activate_context(window);
308	if (!pgctx)
309		return -1;
310
311	vbox = gtk_vbox_new(FALSE, 0);
312
313	notebook = gtk_notebook_new();
314
315	gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
316
317	info_bar = perf_gtk__setup_info_bar();
318	if (info_bar)
319		gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
320
321	statbar = perf_gtk__setup_statusbar();
322	gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
323
324	gtk_container_add(GTK_CONTAINER(window), vbox);
325
326	evlist__for_each(evlist, pos) {
327		struct hists *hists = &pos->hists;
328		const char *evname = perf_evsel__name(pos);
329		GtkWidget *scrolled_window;
330		GtkWidget *tab_label;
331		char buf[512];
332		size_t size = sizeof(buf);
333
334		if (symbol_conf.event_group) {
335			if (!perf_evsel__is_group_leader(pos))
336				continue;
337
338			if (pos->nr_members > 1) {
339				perf_evsel__group_desc(pos, buf, size);
340				evname = buf;
341			}
342		}
343
344		scrolled_window = gtk_scrolled_window_new(NULL, NULL);
345
346		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
347							GTK_POLICY_AUTOMATIC,
348							GTK_POLICY_AUTOMATIC);
349
350		perf_gtk__show_hists(scrolled_window, hists, min_pcnt);
351
352		tab_label = gtk_label_new(evname);
353
354		gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
355	}
356
357	gtk_widget_show_all(window);
358
359	perf_gtk__resize_window(window);
360
361	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
362
363	ui_helpline__push(help);
364
365	gtk_main();
366
367	perf_gtk__deactivate_context(&pgctx);
368
369	return 0;
370}