Linux Audio

Check our new training course

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