2012-12-21 17:20:13 +09:00
# include "../evlist.h"
# include "../cache.h"
# include "../evsel.h"
# include "../sort.h"
# include "../hist.h"
# include "../helpline.h"
# include "gtk.h"
# define MAX_COLUMNS 32
2014-03-03 10:14:04 +09:00
static int __percent_color_snprintf ( struct perf_hpp * hpp , const char * fmt , . . . )
2013-01-22 18:09:36 +09:00
{
int ret = 0 ;
2014-07-31 14:47:36 +09:00
int len ;
2014-03-03 10:14:03 +09:00
va_list args ;
double percent ;
2013-01-22 18:09:36 +09:00
const char * markup ;
2014-03-03 10:14:04 +09:00
char * buf = hpp - > buf ;
size_t size = hpp - > size ;
2013-01-22 18:09:36 +09:00
2014-03-03 10:14:03 +09:00
va_start ( args , fmt ) ;
2014-07-31 14:47:36 +09:00
len = va_arg ( args , int ) ;
2014-03-03 10:14:03 +09:00
percent = va_arg ( args , double ) ;
va_end ( args ) ;
2013-01-22 18:09:36 +09:00
markup = perf_gtk__get_percent_color ( percent ) ;
if ( markup )
ret + = scnprintf ( buf , size , markup ) ;
2014-07-31 14:47:36 +09:00
ret + = scnprintf ( buf + ret , size - ret , fmt , len , percent ) ;
2013-01-22 18:09:36 +09:00
if ( markup )
ret + = scnprintf ( buf + ret , size - ret , " </span> " ) ;
return ret ;
}
# define __HPP_COLOR_PERCENT_FN(_type, _field) \
static u64 he_get_ # # _field ( struct hist_entry * he ) \
2012-12-21 17:20:13 +09:00
{ \
2013-01-22 18:09:36 +09:00
return he - > stat . _field ; \
} \
2012-12-21 17:20:13 +09:00
\
2014-07-31 14:47:38 +09:00
static int perf_gtk__hpp_color_ # # _type ( struct perf_hpp_fmt * fmt , \
2013-01-31 23:31:11 +01:00
struct perf_hpp * hpp , \
2013-01-22 18:09:36 +09:00
struct hist_entry * he ) \
{ \
2014-07-31 14:47:38 +09:00
return hpp__fmt ( fmt , hpp , he , he_get_ # # _field , " %*.2f%% " , \
__percent_color_snprintf , true ) ; \
2012-12-21 17:20:13 +09:00
}
2013-10-30 16:15:23 +09:00
# define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
static u64 he_get_acc_ # # _field ( struct hist_entry * he ) \
{ \
return he - > stat_acc - > _field ; \
} \
\
static int perf_gtk__hpp_color_ # # _type ( struct perf_hpp_fmt * fmt __maybe_unused , \
struct perf_hpp * hpp , \
struct hist_entry * he ) \
{ \
2014-07-31 14:47:38 +09:00
return hpp__fmt_acc ( fmt , hpp , he , he_get_acc_ # # _field , " %*.2f%% " , \
__percent_color_snprintf , true ) ; \
2013-10-30 16:15:23 +09:00
}
2013-01-22 18:09:36 +09:00
__HPP_COLOR_PERCENT_FN ( overhead , period )
__HPP_COLOR_PERCENT_FN ( overhead_sys , period_sys )
__HPP_COLOR_PERCENT_FN ( overhead_us , period_us )
__HPP_COLOR_PERCENT_FN ( overhead_guest_sys , period_guest_sys )
__HPP_COLOR_PERCENT_FN ( overhead_guest_us , period_guest_us )
2013-10-30 16:15:23 +09:00
__HPP_COLOR_ACC_PERCENT_FN ( overhead_acc , period )
2012-12-21 17:20:13 +09:00
2013-01-22 18:09:36 +09:00
# undef __HPP_COLOR_PERCENT_FN
2012-12-21 17:20:13 +09:00
void perf_gtk__init_hpp ( void )
{
perf_hpp__format [ PERF_HPP__OVERHEAD ] . color =
perf_gtk__hpp_color_overhead ;
perf_hpp__format [ PERF_HPP__OVERHEAD_SYS ] . color =
perf_gtk__hpp_color_overhead_sys ;
perf_hpp__format [ PERF_HPP__OVERHEAD_US ] . color =
perf_gtk__hpp_color_overhead_us ;
perf_hpp__format [ PERF_HPP__OVERHEAD_GUEST_SYS ] . color =
perf_gtk__hpp_color_overhead_guest_sys ;
perf_hpp__format [ PERF_HPP__OVERHEAD_GUEST_US ] . color =
perf_gtk__hpp_color_overhead_guest_us ;
2013-10-30 16:15:23 +09:00
perf_hpp__format [ PERF_HPP__OVERHEAD_ACC ] . color =
perf_gtk__hpp_color_overhead_acc ;
2012-12-21 17:20:13 +09:00
}
2013-06-04 18:22:13 +09:00
static void perf_gtk__add_callchain ( struct rb_root * root , GtkTreeStore * store ,
2013-06-04 18:22:14 +09:00
GtkTreeIter * parent , int col , u64 total )
2013-06-04 18:22:13 +09:00
{
struct rb_node * nd ;
bool has_single_node = ( rb_first ( root ) = = rb_last ( root ) ) ;
for ( nd = rb_first ( root ) ; nd ; nd = rb_next ( nd ) ) {
struct callchain_node * node ;
struct callchain_list * chain ;
GtkTreeIter iter , new_parent ;
bool need_new_parent ;
2013-06-04 18:22:14 +09:00
double percent ;
u64 hits , child_total ;
2013-06-04 18:22:13 +09:00
node = rb_entry ( nd , struct callchain_node , rb_node ) ;
2013-06-04 18:22:14 +09:00
hits = callchain_cumul_hits ( node ) ;
percent = 100.0 * hits / total ;
2013-06-04 18:22:13 +09:00
new_parent = * parent ;
need_new_parent = ! has_single_node & & ( node - > val_nr > 1 ) ;
list_for_each_entry ( chain , & node - > val , list ) {
char buf [ 128 ] ;
gtk_tree_store_append ( store , & iter , & new_parent ) ;
2013-06-04 18:22:14 +09:00
scnprintf ( buf , sizeof ( buf ) , " %5.2f%% " , percent ) ;
gtk_tree_store_set ( store , & iter , 0 , buf , - 1 ) ;
2014-11-12 18:05:23 -08:00
callchain_list__sym_name ( chain , buf , sizeof ( buf ) , false ) ;
2013-06-04 18:22:13 +09:00
gtk_tree_store_set ( store , & iter , col , buf , - 1 ) ;
if ( need_new_parent ) {
/*
* Only show the top - most symbol in a callchain
* if it ' s not the only callchain .
*/
new_parent = iter ;
need_new_parent = false ;
}
}
2013-06-04 18:22:14 +09:00
if ( callchain_param . mode = = CHAIN_GRAPH_REL )
child_total = node - > children_hit ;
else
child_total = total ;
2013-06-04 18:22:13 +09:00
/* Now 'iter' contains info of the last callchain_list */
2013-06-04 18:22:14 +09:00
perf_gtk__add_callchain ( & node - > rb_root , store , & iter , col ,
child_total ) ;
2013-06-04 18:22:13 +09:00
}
}
2013-06-04 18:22:16 +09:00
static void on_row_activated ( GtkTreeView * view , GtkTreePath * path ,
GtkTreeViewColumn * col __maybe_unused ,
gpointer user_data __maybe_unused )
{
bool expanded = gtk_tree_view_row_expanded ( view , path ) ;
if ( expanded )
gtk_tree_view_collapse_row ( view , path ) ;
else
gtk_tree_view_expand_row ( view , path , FALSE ) ;
}
2013-05-14 11:09:04 +09:00
static void perf_gtk__show_hists ( GtkWidget * window , struct hists * hists ,
float min_pcnt )
2012-12-21 17:20:13 +09:00
{
struct perf_hpp_fmt * fmt ;
GType col_types [ MAX_COLUMNS ] ;
GtkCellRenderer * renderer ;
2013-06-04 18:22:12 +09:00
GtkTreeStore * store ;
2012-12-21 17:20:13 +09:00
struct rb_node * nd ;
GtkWidget * view ;
int col_idx ;
2013-06-04 18:22:13 +09:00
int sym_col = - 1 ;
2012-12-21 17:20:13 +09:00
int nr_cols ;
char s [ 512 ] ;
struct perf_hpp hpp = {
. buf = s ,
. size = sizeof ( s ) ,
} ;
nr_cols = 0 ;
perf_hpp__for_each_format ( fmt )
col_types [ nr_cols + + ] = G_TYPE_STRING ;
2013-06-04 18:22:12 +09:00
store = gtk_tree_store_newv ( nr_cols , col_types ) ;
2012-12-21 17:20:13 +09:00
view = gtk_tree_view_new ( ) ;
renderer = gtk_cell_renderer_text_new ( ) ;
col_idx = 0 ;
perf_hpp__for_each_format ( fmt ) {
2014-03-18 13:00:59 +09:00
if ( perf_hpp__should_skip ( fmt ) )
continue ;
2014-05-23 18:49:33 +09:00
/*
* XXX no way to determine where symcol column is . .
* Just use last column for now .
*/
if ( perf_hpp__is_sort_entry ( fmt ) )
sym_col = col_idx ;
2012-12-21 17:20:13 +09:00
gtk_tree_view_insert_column_with_attributes ( GTK_TREE_VIEW ( view ) ,
2014-07-31 14:47:40 +09:00
- 1 , fmt - > name ,
2012-12-21 17:20:13 +09:00
renderer , " markup " ,
col_idx + + , NULL ) ;
}
2013-06-04 18:22:15 +09:00
for ( col_idx = 0 ; col_idx < nr_cols ; col_idx + + ) {
2013-06-04 18:22:13 +09:00
GtkTreeViewColumn * column ;
2013-06-04 18:22:15 +09:00
column = gtk_tree_view_get_column ( GTK_TREE_VIEW ( view ) , col_idx ) ;
gtk_tree_view_column_set_resizable ( column , TRUE ) ;
if ( col_idx = = sym_col ) {
gtk_tree_view_set_expander_column ( GTK_TREE_VIEW ( view ) ,
column ) ;
}
2013-06-04 18:22:13 +09:00
}
2012-12-21 17:20:13 +09:00
gtk_tree_view_set_model ( GTK_TREE_VIEW ( view ) , GTK_TREE_MODEL ( store ) ) ;
g_object_unref ( GTK_TREE_MODEL ( store ) ) ;
for ( nd = rb_first ( & hists - > entries ) ; nd ; nd = rb_next ( nd ) ) {
struct hist_entry * h = rb_entry ( nd , struct hist_entry , rb_node ) ;
GtkTreeIter iter ;
2014-01-14 11:52:48 +09:00
u64 total = hists__total_period ( h - > hists ) ;
2013-10-31 10:17:39 +09:00
float percent ;
2012-12-21 17:20:13 +09:00
if ( h - > filtered )
continue ;
2013-10-31 10:17:39 +09:00
percent = hist_entry__get_percent_limit ( h ) ;
2013-05-14 11:09:04 +09:00
if ( percent < min_pcnt )
continue ;
2013-06-04 18:22:12 +09:00
gtk_tree_store_append ( store , & iter , NULL ) ;
2012-12-21 17:20:13 +09:00
col_idx = 0 ;
perf_hpp__for_each_format ( fmt ) {
2014-03-18 13:00:59 +09:00
if ( perf_hpp__should_skip ( fmt ) )
continue ;
2012-12-21 17:20:13 +09:00
if ( fmt - > color )
2013-01-31 23:31:11 +01:00
fmt - > color ( fmt , & hpp , h ) ;
2012-12-21 17:20:13 +09:00
else
2013-01-31 23:31:11 +01:00
fmt - > entry ( fmt , & hpp , h ) ;
2012-12-21 17:20:13 +09:00
2013-06-04 18:22:12 +09:00
gtk_tree_store_set ( store , & iter , col_idx + + , s , - 1 ) ;
2012-12-21 17:20:13 +09:00
}
2013-06-04 18:22:13 +09:00
if ( symbol_conf . use_callchain & & sort__has_sym ) {
2013-06-04 18:22:14 +09:00
if ( callchain_param . mode = = CHAIN_GRAPH_REL )
2014-05-23 18:49:33 +09:00
total = symbol_conf . cumulate_callchain ?
h - > stat_acc - > period : h - > stat . period ;
2013-06-04 18:22:14 +09:00
2013-06-04 18:22:13 +09:00
perf_gtk__add_callchain ( & h - > sorted_chain , store , & iter ,
2013-06-04 18:22:14 +09:00
sym_col , total ) ;
2013-06-04 18:22:13 +09:00
}
2012-12-21 17:20:13 +09:00
}
2013-06-04 18:22:17 +09:00
gtk_tree_view_set_rules_hint ( GTK_TREE_VIEW ( view ) , TRUE ) ;
2013-06-04 18:22:16 +09:00
g_signal_connect ( view , " row-activated " ,
G_CALLBACK ( on_row_activated ) , NULL ) ;
2012-12-21 17:20:13 +09:00
gtk_container_add ( GTK_CONTAINER ( window ) , view ) ;
}
int perf_evlist__gtk_browse_hists ( struct perf_evlist * evlist ,
const char * help ,
2013-05-14 11:09:04 +09:00
struct hist_browser_timer * hbt __maybe_unused ,
float min_pcnt )
2012-12-21 17:20:13 +09:00
{
struct perf_evsel * pos ;
GtkWidget * vbox ;
GtkWidget * notebook ;
GtkWidget * info_bar ;
GtkWidget * statbar ;
GtkWidget * window ;
signal ( SIGSEGV , perf_gtk__signal ) ;
signal ( SIGFPE , perf_gtk__signal ) ;
signal ( SIGINT , perf_gtk__signal ) ;
signal ( SIGQUIT , perf_gtk__signal ) ;
signal ( SIGTERM , perf_gtk__signal ) ;
window = gtk_window_new ( GTK_WINDOW_TOPLEVEL ) ;
gtk_window_set_title ( GTK_WINDOW ( window ) , " perf report " ) ;
g_signal_connect ( window , " delete_event " , gtk_main_quit , NULL ) ;
pgctx = perf_gtk__activate_context ( window ) ;
if ( ! pgctx )
return - 1 ;
vbox = gtk_vbox_new ( FALSE , 0 ) ;
notebook = gtk_notebook_new ( ) ;
2012-12-21 17:20:14 +09:00
gtk_box_pack_start ( GTK_BOX ( vbox ) , notebook , TRUE , TRUE , 0 ) ;
info_bar = perf_gtk__setup_info_bar ( ) ;
if ( info_bar )
gtk_box_pack_start ( GTK_BOX ( vbox ) , info_bar , FALSE , FALSE , 0 ) ;
statbar = perf_gtk__setup_statusbar ( ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , statbar , FALSE , FALSE , 0 ) ;
gtk_container_add ( GTK_CONTAINER ( window ) , vbox ) ;
2014-01-10 10:37:27 -03:00
evlist__for_each ( evlist , pos ) {
2014-10-09 13:13:41 -03:00
struct hists * hists = evsel__hists ( pos ) ;
2012-12-21 17:20:13 +09:00
const char * evname = perf_evsel__name ( pos ) ;
GtkWidget * scrolled_window ;
GtkWidget * tab_label ;
2013-01-22 18:09:44 +09:00
char buf [ 512 ] ;
size_t size = sizeof ( buf ) ;
2012-12-21 17:20:13 +09:00
2013-01-22 18:09:44 +09:00
if ( symbol_conf . event_group ) {
if ( ! perf_evsel__is_group_leader ( pos ) )
continue ;
if ( pos - > nr_members > 1 ) {
perf_evsel__group_desc ( pos , buf , size ) ;
evname = buf ;
}
}
2013-01-22 18:09:43 +09:00
2012-12-21 17:20:13 +09:00
scrolled_window = gtk_scrolled_window_new ( NULL , NULL ) ;
gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW ( scrolled_window ) ,
GTK_POLICY_AUTOMATIC ,
GTK_POLICY_AUTOMATIC ) ;
2013-05-14 11:09:04 +09:00
perf_gtk__show_hists ( scrolled_window , hists , min_pcnt ) ;
2012-12-21 17:20:13 +09:00
tab_label = gtk_label_new ( evname ) ;
gtk_notebook_append_page ( GTK_NOTEBOOK ( notebook ) , scrolled_window , tab_label ) ;
}
gtk_widget_show_all ( window ) ;
perf_gtk__resize_window ( window ) ;
gtk_window_set_position ( GTK_WINDOW ( window ) , GTK_WIN_POS_CENTER ) ;
ui_helpline__push ( help ) ;
gtk_main ( ) ;
perf_gtk__deactivate_context ( & pgctx ) ;
return 0 ;
}