2010-03-11 20:12:44 -03:00
# define _GNU_SOURCE
# include <stdio.h>
# undef _GNU_SOURCE
2010-05-11 23:18:06 -03:00
# include <slang.h>
2010-03-11 20:12:44 -03:00
# include <stdlib.h>
# include <newt.h>
2010-03-12 10:48:12 -03:00
# include <sys/ttydefaults.h>
2010-03-11 20:12:44 -03:00
# include "cache.h"
# include "hist.h"
# include "session.h"
# include "sort.h"
# include "symbol.h"
2010-03-26 21:16:22 -03:00
struct ui_progress {
newtComponent form , scale ;
} ;
struct ui_progress * ui_progress__new ( const char * title , u64 total )
{
struct ui_progress * self = malloc ( sizeof ( * self ) ) ;
if ( self ! = NULL ) {
int cols ;
newtGetScreenSize ( & cols , NULL ) ;
cols - = 4 ;
newtCenteredWindow ( cols , 1 , title ) ;
self - > form = newtForm ( NULL , NULL , 0 ) ;
if ( self - > form = = NULL )
goto out_free_self ;
self - > scale = newtScale ( 0 , 0 , cols , total ) ;
if ( self - > scale = = NULL )
goto out_free_form ;
2010-05-10 10:51:25 -03:00
newtFormAddComponent ( self - > form , self - > scale ) ;
2010-03-26 21:16:22 -03:00
newtRefresh ( ) ;
}
return self ;
out_free_form :
newtFormDestroy ( self - > form ) ;
out_free_self :
free ( self ) ;
return NULL ;
}
void ui_progress__update ( struct ui_progress * self , u64 curr )
{
newtScaleSet ( self - > scale , curr ) ;
newtRefresh ( ) ;
}
void ui_progress__delete ( struct ui_progress * self )
{
newtFormDestroy ( self - > form ) ;
newtPopWindow ( ) ;
free ( self ) ;
}
2010-05-11 18:01:23 -03:00
static void ui_helpline__pop ( void )
{
newtPopHelpLine ( ) ;
}
static void ui_helpline__push ( const char * msg )
{
newtPushHelpLine ( msg ) ;
}
static void ui_helpline__vpush ( const char * fmt , va_list ap )
{
char * s ;
if ( vasprintf ( & s , fmt , ap ) < 0 )
vfprintf ( stderr , fmt , ap ) ;
else {
ui_helpline__push ( s ) ;
free ( s ) ;
}
}
static void ui_helpline__fpush ( const char * fmt , . . . )
{
va_list ap ;
va_start ( ap , fmt ) ;
ui_helpline__vpush ( fmt , ap ) ;
va_end ( ap ) ;
}
static void ui_helpline__puts ( const char * msg )
{
ui_helpline__pop ( ) ;
ui_helpline__push ( msg ) ;
}
2010-03-26 21:16:22 -03:00
static char browser__last_msg [ 1024 ] ;
int browser__show_help ( const char * format , va_list ap )
{
int ret ;
static int backlog ;
ret = vsnprintf ( browser__last_msg + backlog ,
sizeof ( browser__last_msg ) - backlog , format , ap ) ;
backlog + = ret ;
if ( browser__last_msg [ backlog - 1 ] = = ' \n ' ) {
2010-05-11 18:01:23 -03:00
ui_helpline__puts ( browser__last_msg ) ;
2010-03-26 21:16:22 -03:00
newtRefresh ( ) ;
backlog = 0 ;
}
return ret ;
}
2010-03-12 10:48:12 -03:00
static void newt_form__set_exit_keys ( newtComponent self )
{
newtFormAddHotKey ( self , NEWT_KEY_ESCAPE ) ;
newtFormAddHotKey ( self , ' Q ' ) ;
newtFormAddHotKey ( self , ' q ' ) ;
newtFormAddHotKey ( self , CTRL ( ' c ' ) ) ;
}
static newtComponent newt_form__new ( void )
{
newtComponent self = newtForm ( NULL , NULL , 0 ) ;
if ( self )
newt_form__set_exit_keys ( self ) ;
return self ;
}
2010-04-03 16:30:44 -03:00
static int popup_menu ( int argc , char * const argv [ ] )
2010-03-24 16:40:14 -03:00
{
struct newtExitStruct es ;
int i , rc = - 1 , max_len = 5 ;
newtComponent listbox , form = newt_form__new ( ) ;
if ( form = = NULL )
return - 1 ;
listbox = newtListbox ( 0 , 0 , argc , NEWT_FLAG_RETURNEXIT ) ;
if ( listbox = = NULL )
goto out_destroy_form ;
2010-05-10 10:51:25 -03:00
newtFormAddComponent ( form , listbox ) ;
2010-03-24 16:40:14 -03:00
for ( i = 0 ; i < argc ; + + i ) {
int len = strlen ( argv [ i ] ) ;
if ( len > max_len )
max_len = len ;
if ( newtListboxAddEntry ( listbox , argv [ i ] , ( void * ) ( long ) i ) )
goto out_destroy_form ;
}
newtCenteredWindow ( max_len , argc , NULL ) ;
newtFormRun ( form , & es ) ;
rc = newtListboxGetCurrent ( listbox ) - NULL ;
if ( es . reason = = NEWT_EXIT_HOTKEY )
rc = - 1 ;
newtPopWindow ( ) ;
out_destroy_form :
newtFormDestroy ( form ) ;
return rc ;
}
static bool dialog_yesno ( const char * msg )
{
/* newtWinChoice should really be accepting const char pointers... */
char yes [ ] = " Yes " , no [ ] = " No " ;
2010-04-05 12:04:23 -03:00
return newtWinChoice ( NULL , yes , no , ( char * ) msg ) = = 1 ;
2010-03-24 16:40:14 -03:00
}
2010-05-11 23:18:06 -03:00
# define HE_COLORSET_TOP 50
# define HE_COLORSET_MEDIUM 51
# define HE_COLORSET_NORMAL 52
# define HE_COLORSET_SELECTED 53
# define HE_COLORSET_CODE 54
static int ui_browser__percent_color ( double percent , bool current )
{
if ( current )
return HE_COLORSET_SELECTED ;
if ( percent > = MIN_RED )
return HE_COLORSET_TOP ;
if ( percent > = MIN_GREEN )
return HE_COLORSET_MEDIUM ;
return HE_COLORSET_NORMAL ;
}
struct ui_browser {
newtComponent form , sb ;
u64 index , first_visible_entry_idx ;
void * first_visible_entry , * entries ;
u16 top , left , width , height ;
void * priv ;
u32 nr_entries ;
} ;
static void ui_browser__refresh_dimensions ( struct ui_browser * self )
{
int cols , rows ;
newtGetScreenSize ( & cols , & rows ) ;
if ( self - > width > cols - 4 )
self - > width = cols - 4 ;
self - > height = rows - 5 ;
if ( self - > height > self - > nr_entries )
self - > height = self - > nr_entries ;
self - > top = ( rows - self - > height ) / 2 ;
self - > left = ( cols - self - > width ) / 2 ;
}
static void ui_browser__reset_index ( struct ui_browser * self )
{
self - > index = self - > first_visible_entry_idx = 0 ;
self - > first_visible_entry = NULL ;
}
static int objdump_line__show ( struct objdump_line * self , struct list_head * head ,
int width , struct hist_entry * he , int len ,
bool current_entry )
{
if ( self - > offset ! = - 1 ) {
struct symbol * sym = he - > ms . sym ;
unsigned int hits = 0 ;
double percent = 0.0 ;
int color ;
struct sym_priv * priv = symbol__priv ( sym ) ;
struct sym_ext * sym_ext = priv - > ext ;
struct sym_hist * h = priv - > hist ;
s64 offset = self - > offset ;
struct objdump_line * next = objdump__get_next_ip_line ( head , self ) ;
while ( offset < ( s64 ) len & &
( next = = NULL | | offset < next - > offset ) ) {
if ( sym_ext ) {
percent + = sym_ext [ offset ] . percent ;
} else
hits + = h - > ip [ offset ] ;
+ + offset ;
}
if ( sym_ext = = NULL & & h - > sum )
percent = 100.0 * hits / h - > sum ;
color = ui_browser__percent_color ( percent , current_entry ) ;
SLsmg_set_color ( color ) ;
SLsmg_printf ( " %7.2f " , percent ) ;
if ( ! current_entry )
SLsmg_set_color ( HE_COLORSET_CODE ) ;
} else {
int color = ui_browser__percent_color ( 0 , current_entry ) ;
SLsmg_set_color ( color ) ;
SLsmg_write_nstring ( " " , 9 ) ;
}
SLsmg_write_char ( ' : ' ) ;
SLsmg_write_nstring ( " " , 8 ) ;
if ( ! * self - > line )
SLsmg_write_nstring ( " " , width - 18 ) ;
else
SLsmg_write_nstring ( self - > line , width - 18 ) ;
return 0 ;
}
static int ui_browser__refresh_entries ( struct ui_browser * self )
{
struct objdump_line * pos ;
struct list_head * head = self - > entries ;
struct hist_entry * he = self - > priv ;
int row = 0 ;
int len = he - > ms . sym - > end - he - > ms . sym - > start ;
if ( self - > first_visible_entry = = NULL | | self - > first_visible_entry = = self - > entries )
self - > first_visible_entry = head - > next ;
pos = list_entry ( self - > first_visible_entry , struct objdump_line , node ) ;
list_for_each_entry_from ( pos , head , node ) {
bool current_entry = ( self - > first_visible_entry_idx + row ) = = self - > index ;
SLsmg_gotorc ( self - > top + row , self - > left ) ;
objdump_line__show ( pos , head , self - > width ,
he , len , current_entry ) ;
if ( + + row = = self - > height )
break ;
}
SLsmg_set_color ( HE_COLORSET_NORMAL ) ;
SLsmg_fill_region ( self - > top + row , self - > left ,
self - > height - row , self - > width , ' ' ) ;
return 0 ;
}
static int ui_browser__run ( struct ui_browser * self , const char * title ,
struct newtExitStruct * es )
{
if ( self - > form ) {
newtFormDestroy ( self - > form ) ;
newtPopWindow ( ) ;
}
ui_browser__refresh_dimensions ( self ) ;
newtCenteredWindow ( self - > width + 2 , self - > height , title ) ;
self - > form = newt_form__new ( ) ;
if ( self - > form = = NULL )
return - 1 ;
self - > sb = newtVerticalScrollbar ( self - > width + 1 , 0 , self - > height ,
HE_COLORSET_NORMAL ,
HE_COLORSET_SELECTED ) ;
if ( self - > sb = = NULL )
return - 1 ;
newtFormAddHotKey ( self - > form , NEWT_KEY_UP ) ;
newtFormAddHotKey ( self - > form , NEWT_KEY_DOWN ) ;
newtFormAddHotKey ( self - > form , NEWT_KEY_PGUP ) ;
newtFormAddHotKey ( self - > form , NEWT_KEY_PGDN ) ;
newtFormAddHotKey ( self - > form , NEWT_KEY_HOME ) ;
newtFormAddHotKey ( self - > form , NEWT_KEY_END ) ;
if ( ui_browser__refresh_entries ( self ) < 0 )
return - 1 ;
newtFormAddComponent ( self - > form , self - > sb ) ;
while ( 1 ) {
unsigned int offset ;
newtFormRun ( self - > form , es ) ;
if ( es - > reason ! = NEWT_EXIT_HOTKEY )
break ;
switch ( es - > u . key ) {
case NEWT_KEY_DOWN :
if ( self - > index = = self - > nr_entries - 1 )
break ;
+ + self - > index ;
if ( self - > index = = self - > first_visible_entry_idx + self - > height ) {
struct list_head * pos = self - > first_visible_entry ;
+ + self - > first_visible_entry_idx ;
self - > first_visible_entry = pos - > next ;
}
break ;
case NEWT_KEY_UP :
if ( self - > index = = 0 )
break ;
- - self - > index ;
if ( self - > index < self - > first_visible_entry_idx ) {
struct list_head * pos = self - > first_visible_entry ;
- - self - > first_visible_entry_idx ;
self - > first_visible_entry = pos - > prev ;
}
break ;
case NEWT_KEY_PGDN :
if ( self - > first_visible_entry_idx + self - > height > self - > nr_entries - 1 )
break ;
offset = self - > height ;
if ( self - > index + offset > self - > nr_entries - 1 )
offset = self - > nr_entries - 1 - self - > index ;
self - > index + = offset ;
self - > first_visible_entry_idx + = offset ;
while ( offset - - ) {
struct list_head * pos = self - > first_visible_entry ;
self - > first_visible_entry = pos - > next ;
}
break ;
case NEWT_KEY_PGUP :
if ( self - > first_visible_entry_idx = = 0 )
break ;
if ( self - > first_visible_entry_idx < self - > height )
offset = self - > first_visible_entry_idx ;
else
offset = self - > height ;
self - > index - = offset ;
self - > first_visible_entry_idx - = offset ;
while ( offset - - ) {
struct list_head * pos = self - > first_visible_entry ;
self - > first_visible_entry = pos - > prev ;
}
break ;
case NEWT_KEY_HOME :
ui_browser__reset_index ( self ) ;
break ;
case NEWT_KEY_END : {
struct list_head * head = self - > entries ;
offset = self - > height - 1 ;
if ( offset > self - > nr_entries )
offset = self - > nr_entries ;
self - > index = self - > first_visible_entry_idx = self - > nr_entries - 1 - offset ;
self - > first_visible_entry = head - > prev ;
while ( offset - - ! = 0 ) {
struct list_head * pos = self - > first_visible_entry ;
self - > first_visible_entry = pos - > prev ;
}
}
break ;
case NEWT_KEY_ESCAPE :
case CTRL ( ' c ' ) :
case ' Q ' :
case ' q ' :
return 0 ;
default :
continue ;
}
if ( ui_browser__refresh_entries ( self ) < 0 )
return - 1 ;
}
return 0 ;
}
2010-03-22 17:52:49 -03:00
/*
* When debugging newt problems it was useful to be able to " unroll "
* the calls to newtCheckBoxTreeAdd { Array , Item } , so that we can generate
* a source file with the sequence of calls to these methods , to then
* tweak the arrays to get the intended results , so I ' m keeping this code
* here , may be useful again in the future .
*/
# undef NEWT_DEBUG
static void newt_checkbox_tree__add ( newtComponent tree , const char * str ,
void * priv , int * indexes )
{
# ifdef NEWT_DEBUG
/* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
int i = 0 , len = 40 - strlen ( str ) ;
fprintf ( stderr ,
" \t newtCheckboxTreeAddItem(tree, %*.*s \" %s \" , (void *)%p, 0, " ,
len , len , " " , str , priv ) ;
while ( indexes [ i ] ! = NEWT_ARG_LAST ) {
if ( indexes [ i ] ! = NEWT_ARG_APPEND )
fprintf ( stderr , " %d, " , indexes [ i ] ) ;
else
fprintf ( stderr , " %s, " , " NEWT_ARG_APPEND " ) ;
+ + i ;
}
fprintf ( stderr , " %s " , " NEWT_ARG_LAST); \n " ) ;
fflush ( stderr ) ;
# endif
newtCheckboxTreeAddArray ( tree , str , priv , 0 , indexes ) ;
}
static char * callchain_list__sym_name ( struct callchain_list * self ,
char * bf , size_t bfsize )
{
2010-03-24 16:40:18 -03:00
if ( self - > ms . sym )
return self - > ms . sym - > name ;
2010-03-22 17:52:49 -03:00
snprintf ( bf , bfsize , " %#Lx " , self - > ip ) ;
return bf ;
}
static void __callchain__append_graph_browser ( struct callchain_node * self ,
newtComponent tree , u64 total ,
int * indexes , int depth )
{
struct rb_node * node ;
u64 new_total , remaining ;
int idx = 0 ;
if ( callchain_param . mode = = CHAIN_GRAPH_REL )
new_total = self - > children_hit ;
else
new_total = total ;
remaining = new_total ;
node = rb_first ( & self - > rb_root ) ;
while ( node ) {
struct callchain_node * child = rb_entry ( node , struct callchain_node , rb_node ) ;
struct rb_node * next = rb_next ( node ) ;
u64 cumul = cumul_hits ( child ) ;
struct callchain_list * chain ;
int first = true , printed = 0 ;
int chain_idx = - 1 ;
remaining - = cumul ;
indexes [ depth ] = NEWT_ARG_APPEND ;
indexes [ depth + 1 ] = NEWT_ARG_LAST ;
list_for_each_entry ( chain , & child - > val , list ) {
char ipstr [ BITS_PER_LONG / 4 + 1 ] ,
* alloc_str = NULL ;
const char * str = callchain_list__sym_name ( chain , ipstr , sizeof ( ipstr ) ) ;
if ( first ) {
double percent = cumul * 100.0 / new_total ;
first = false ;
if ( asprintf ( & alloc_str , " %2.2f%% %s " , percent , str ) < 0 )
str = " Not enough memory! " ;
else
str = alloc_str ;
} else {
indexes [ depth ] = idx ;
indexes [ depth + 1 ] = NEWT_ARG_APPEND ;
indexes [ depth + 2 ] = NEWT_ARG_LAST ;
+ + chain_idx ;
}
2010-03-24 16:40:19 -03:00
newt_checkbox_tree__add ( tree , str , & chain - > ms , indexes ) ;
2010-03-22 17:52:49 -03:00
free ( alloc_str ) ;
+ + printed ;
}
indexes [ depth ] = idx ;
if ( chain_idx ! = - 1 )
indexes [ depth + 1 ] = chain_idx ;
if ( printed ! = 0 )
+ + idx ;
__callchain__append_graph_browser ( child , tree , new_total , indexes ,
depth + ( chain_idx ! = - 1 ? 2 : 1 ) ) ;
node = next ;
}
}
static void callchain__append_graph_browser ( struct callchain_node * self ,
newtComponent tree , u64 total ,
int * indexes , int parent_idx )
{
struct callchain_list * chain ;
int i = 0 ;
indexes [ 1 ] = NEWT_ARG_APPEND ;
indexes [ 2 ] = NEWT_ARG_LAST ;
list_for_each_entry ( chain , & self - > val , list ) {
char ipstr [ BITS_PER_LONG / 4 + 1 ] , * str ;
if ( chain - > ip > = PERF_CONTEXT_MAX )
continue ;
if ( ! i + + & & sort__first_dimension = = SORT_SYM )
continue ;
str = callchain_list__sym_name ( chain , ipstr , sizeof ( ipstr ) ) ;
2010-03-24 16:40:19 -03:00
newt_checkbox_tree__add ( tree , str , & chain - > ms , indexes ) ;
2010-03-22 17:52:49 -03:00
}
indexes [ 1 ] = parent_idx ;
indexes [ 2 ] = NEWT_ARG_APPEND ;
indexes [ 3 ] = NEWT_ARG_LAST ;
__callchain__append_graph_browser ( self , tree , total , indexes , 2 ) ;
}
static void hist_entry__append_callchain_browser ( struct hist_entry * self ,
newtComponent tree , u64 total , int parent_idx )
{
struct rb_node * rb_node ;
int indexes [ 1024 ] = { [ 0 ] = parent_idx , } ;
int idx = 0 ;
struct callchain_node * chain ;
rb_node = rb_first ( & self - > sorted_chain ) ;
while ( rb_node ) {
chain = rb_entry ( rb_node , struct callchain_node , rb_node ) ;
switch ( callchain_param . mode ) {
case CHAIN_FLAT :
break ;
case CHAIN_GRAPH_ABS : /* falldown */
case CHAIN_GRAPH_REL :
callchain__append_graph_browser ( chain , tree , total , indexes , idx + + ) ;
break ;
case CHAIN_NONE :
default :
break ;
}
rb_node = rb_next ( rb_node ) ;
}
}
2010-03-11 20:12:44 -03:00
static size_t hist_entry__append_browser ( struct hist_entry * self ,
2010-03-22 17:52:49 -03:00
newtComponent tree , u64 total )
2010-03-11 20:12:44 -03:00
{
2010-03-31 11:33:40 -03:00
char s [ 256 ] ;
size_t ret ;
2010-03-11 20:12:44 -03:00
if ( symbol_conf . exclude_other & & ! self - > parent )
return 0 ;
2010-03-31 11:33:40 -03:00
ret = hist_entry__snprintf ( self , s , sizeof ( s ) , NULL ,
false , 0 , false , total ) ;
2010-03-22 17:52:49 -03:00
if ( symbol_conf . use_callchain ) {
int indexes [ 2 ] ;
indexes [ 0 ] = NEWT_ARG_APPEND ;
indexes [ 1 ] = NEWT_ARG_LAST ;
2010-03-24 16:40:19 -03:00
newt_checkbox_tree__add ( tree , s , & self - > ms , indexes ) ;
2010-03-22 17:52:49 -03:00
} else
2010-03-24 16:40:19 -03:00
newtListboxAppendEntry ( tree , s , & self - > ms ) ;
2010-03-22 17:52:49 -03:00
2010-03-31 11:33:40 -03:00
return ret ;
2010-03-11 20:12:44 -03:00
}
2010-05-11 23:18:06 -03:00
static void hist_entry__annotate_browser ( struct hist_entry * self )
2010-03-11 20:12:44 -03:00
{
2010-05-11 23:18:06 -03:00
struct ui_browser browser ;
2010-03-11 20:12:44 -03:00
struct newtExitStruct es ;
2010-05-11 23:18:06 -03:00
struct objdump_line * pos , * n ;
LIST_HEAD ( head ) ;
2010-03-11 20:12:44 -03:00
2010-05-11 23:18:06 -03:00
if ( self - > ms . sym = = NULL )
2010-03-11 20:12:44 -03:00
return ;
2010-05-11 23:18:06 -03:00
if ( hist_entry__annotate ( self , & head ) < 0 )
2010-03-11 20:12:44 -03:00
return ;
2010-05-11 18:01:23 -03:00
ui_helpline__push ( " Press ESC to exit " ) ;
2010-03-11 20:12:44 -03:00
2010-05-11 23:18:06 -03:00
memset ( & browser , 0 , sizeof ( browser ) ) ;
browser . entries = & head ;
browser . priv = self ;
list_for_each_entry ( pos , & head , node ) {
size_t line_len = strlen ( pos - > line ) ;
if ( browser . width < line_len )
browser . width = line_len ;
+ + browser . nr_entries ;
2010-03-11 20:12:44 -03:00
}
2010-05-11 23:18:06 -03:00
browser . width + = 18 ; /* Percentage */
ui_browser__run ( & browser , self - > ms . sym - > name , & es ) ;
newtFormDestroy ( browser . form ) ;
2010-03-11 20:12:44 -03:00
newtPopWindow ( ) ;
2010-05-11 23:18:06 -03:00
list_for_each_entry_safe ( pos , n , & head , node ) {
list_del ( & pos - > node ) ;
objdump_line__free ( pos ) ;
}
2010-05-11 18:01:23 -03:00
ui_helpline__pop ( ) ;
2010-03-11 20:12:44 -03:00
}
2010-03-24 16:40:14 -03:00
static const void * newt__symbol_tree_get_current ( newtComponent self )
{
if ( symbol_conf . use_callchain )
return newtCheckboxTreeGetCurrent ( self ) ;
return newtListboxGetCurrent ( self ) ;
}
2010-04-03 11:25:56 -03:00
static void hist_browser__selection ( newtComponent self , void * data )
2010-03-24 16:40:14 -03:00
{
2010-03-24 16:40:19 -03:00
const struct map_symbol * * symbol_ptr = data ;
2010-03-24 16:40:14 -03:00
* symbol_ptr = newt__symbol_tree_get_current ( self ) ;
}
2010-04-03 11:25:56 -03:00
struct hist_browser {
newtComponent form , tree ;
const struct map_symbol * selection ;
} ;
static struct hist_browser * hist_browser__new ( void )
{
struct hist_browser * self = malloc ( sizeof ( * self ) ) ;
2010-04-03 16:30:44 -03:00
if ( self ! = NULL )
self - > form = NULL ;
2010-04-03 11:25:56 -03:00
return self ;
}
static void hist_browser__delete ( struct hist_browser * self )
{
newtFormDestroy ( self - > form ) ;
newtPopWindow ( ) ;
free ( self ) ;
}
2010-05-11 11:10:15 -03:00
static int hist_browser__populate ( struct hist_browser * self , struct hists * hists ,
const char * title )
2010-03-11 20:12:44 -03:00
{
2010-04-03 11:25:56 -03:00
int max_len = 0 , idx , cols , rows ;
struct ui_progress * progress ;
2010-03-11 20:12:44 -03:00
struct rb_node * nd ;
2010-03-26 21:16:22 -03:00
u64 curr_hist = 0 ;
2010-05-14 14:19:35 -03:00
char seq [ ] = " . " , unit ;
2010-04-03 16:30:44 -03:00
char str [ 256 ] ;
2010-05-14 14:19:35 -03:00
unsigned long nr_events = hists - > stats . nr_events [ PERF_RECORD_SAMPLE ] ;
2010-04-03 16:30:44 -03:00
if ( self - > form ) {
newtFormDestroy ( self - > form ) ;
newtPopWindow ( ) ;
}
2010-05-14 14:19:35 -03:00
nr_events = convert_unit ( nr_events , & unit ) ;
snprintf ( str , sizeof ( str ) , " Events: %lu%c " ,
nr_events , unit ) ;
2010-04-03 16:30:44 -03:00
newtDrawRootText ( 0 , 0 , str ) ;
newtGetScreenSize ( NULL , & rows ) ;
if ( symbol_conf . use_callchain )
self - > tree = newtCheckboxTreeMulti ( 0 , 0 , rows - 5 , seq ,
NEWT_FLAG_SCROLL ) ;
else
self - > tree = newtListbox ( 0 , 0 , rows - 5 ,
( NEWT_FLAG_SCROLL |
NEWT_FLAG_RETURNEXIT ) ) ;
newtComponentAddCallback ( self - > tree , hist_browser__selection ,
& self - > selection ) ;
2010-03-26 21:16:22 -03:00
2010-05-11 11:10:15 -03:00
progress = ui_progress__new ( " Adding entries to the browser... " ,
hists - > nr_entries ) ;
2010-03-26 21:16:22 -03:00
if ( progress = = NULL )
return - 1 ;
2010-03-11 20:12:44 -03:00
2010-03-22 17:52:49 -03:00
idx = 0 ;
2010-05-11 11:10:15 -03:00
for ( nd = rb_first ( & hists - > entries ) ; nd ; nd = rb_next ( nd ) ) {
2010-03-11 20:12:44 -03:00
struct hist_entry * h = rb_entry ( nd , struct hist_entry , rb_node ) ;
2010-04-03 16:30:44 -03:00
int len ;
if ( h - > filtered )
continue ;
2010-05-14 13:16:55 -03:00
len = hist_entry__append_browser ( h , self - > tree , hists - > stats . total_period ) ;
2010-03-11 20:12:44 -03:00
if ( len > max_len )
max_len = len ;
2010-03-24 16:40:19 -03:00
if ( symbol_conf . use_callchain )
2010-04-03 11:25:56 -03:00
hist_entry__append_callchain_browser ( h , self - > tree ,
2010-05-14 13:16:55 -03:00
hists - > stats . total_period , idx + + ) ;
2010-03-26 21:16:22 -03:00
+ + curr_hist ;
if ( curr_hist % 5 )
ui_progress__update ( progress , curr_hist ) ;
2010-03-11 20:12:44 -03:00
}
2010-03-26 21:16:22 -03:00
ui_progress__delete ( progress ) ;
2010-04-03 11:25:56 -03:00
newtGetScreenSize ( & cols , & rows ) ;
2010-03-22 17:52:49 -03:00
if ( max_len > cols )
max_len = cols - 3 ;
if ( ! symbol_conf . use_callchain )
2010-04-03 11:25:56 -03:00
newtListboxSetWidth ( self - > tree , max_len ) ;
2010-03-22 17:52:49 -03:00
newtCenteredWindow ( max_len + ( symbol_conf . use_callchain ? 5 : 0 ) ,
2010-04-05 12:02:18 -03:00
rows - 5 , title ) ;
2010-04-03 11:25:56 -03:00
self - > form = newt_form__new ( ) ;
2010-04-03 16:30:44 -03:00
if ( self - > form = = NULL )
return - 1 ;
2010-04-03 11:25:56 -03:00
newtFormAddHotKey ( self - > form , ' A ' ) ;
newtFormAddHotKey ( self - > form , ' a ' ) ;
newtFormAddHotKey ( self - > form , NEWT_KEY_RIGHT ) ;
newtFormAddComponents ( self - > form , self - > tree , NULL ) ;
self - > selection = newt__symbol_tree_get_current ( self - > tree ) ;
return 0 ;
}
2010-05-11 23:18:06 -03:00
static struct hist_entry * hist_browser__selected_entry ( struct hist_browser * self )
2010-04-03 22:44:37 -03:00
{
int * indexes ;
if ( ! symbol_conf . use_callchain )
goto out ;
indexes = newtCheckboxTreeFindItem ( self - > tree , ( void * ) self - > selection ) ;
if ( indexes ) {
bool is_hist_entry = indexes [ 1 ] = = NEWT_ARG_LAST ;
free ( indexes ) ;
if ( is_hist_entry )
goto out ;
}
return NULL ;
out :
2010-05-11 23:18:06 -03:00
return container_of ( self - > selection , struct hist_entry , ms ) ;
}
static struct thread * hist_browser__selected_thread ( struct hist_browser * self )
{
struct hist_entry * he = hist_browser__selected_entry ( self ) ;
return he ? he - > thread : NULL ;
2010-04-03 22:44:37 -03:00
}
2010-04-05 12:02:18 -03:00
static int hist_browser__title ( char * bf , size_t size , const char * input_name ,
const struct dso * dso , const struct thread * thread )
{
int printed = 0 ;
if ( thread )
printed + = snprintf ( bf + printed , size - printed ,
" Thread: %s(%d) " ,
( thread - > comm_set ? thread - > comm : " " ) ,
thread - > pid ) ;
if ( dso )
printed + = snprintf ( bf + printed , size - printed ,
" %sDSO: %s " , thread ? " " : " " ,
dso - > short_name ) ;
return printed ? : snprintf ( bf , size , " Report: %s " , input_name ) ;
}
2010-05-11 11:10:15 -03:00
int hists__browse ( struct hists * self , const char * helpline , const char * input_name )
2010-04-03 11:25:56 -03:00
{
2010-04-05 12:02:18 -03:00
struct hist_browser * browser = hist_browser__new ( ) ;
const struct thread * thread_filter = NULL ;
const struct dso * dso_filter = NULL ;
2010-04-03 11:25:56 -03:00
struct newtExitStruct es ;
2010-04-05 12:02:18 -03:00
char msg [ 160 ] ;
2010-04-03 11:25:56 -03:00
int err = - 1 ;
if ( browser = = NULL )
return - 1 ;
2010-05-11 18:01:23 -03:00
ui_helpline__push ( helpline ) ;
2010-04-03 11:25:56 -03:00
2010-04-05 12:02:18 -03:00
hist_browser__title ( msg , sizeof ( msg ) , input_name ,
dso_filter , thread_filter ) ;
2010-05-11 11:10:15 -03:00
if ( hist_browser__populate ( browser , self , msg ) < 0 )
2010-04-03 11:25:56 -03:00
goto out ;
2010-03-11 20:12:44 -03:00
while ( 1 ) {
2010-04-03 22:44:37 -03:00
const struct thread * thread ;
2010-04-05 12:02:18 -03:00
const struct dso * dso ;
2010-04-03 16:30:44 -03:00
char * options [ 16 ] ;
int nr_options = 0 , choice = 0 , i ,
2010-04-03 22:44:37 -03:00
annotate = - 2 , zoom_dso = - 2 , zoom_thread = - 2 ;
2010-03-11 20:12:44 -03:00
2010-04-03 11:25:56 -03:00
newtFormRun ( browser - > form , & es ) ;
2010-03-24 16:40:14 -03:00
if ( es . reason = = NEWT_EXIT_HOTKEY ) {
2010-03-24 16:40:19 -03:00
if ( toupper ( es . u . key ) = = ' A ' )
goto do_annotate ;
2010-03-24 16:40:14 -03:00
if ( es . u . key = = NEWT_KEY_ESCAPE | |
toupper ( es . u . key ) = = ' Q ' | |
es . u . key = = CTRL ( ' c ' ) ) {
if ( dialog_yesno ( " Do you really want to exit? " ) )
break ;
else
continue ;
}
}
2010-04-03 16:30:44 -03:00
if ( browser - > selection - > sym ! = NULL & &
asprintf ( & options [ nr_options ] , " Annotate %s " ,
browser - > selection - > sym - > name ) > 0 )
annotate = nr_options + + ;
2010-04-03 22:44:37 -03:00
thread = hist_browser__selected_thread ( browser ) ;
if ( thread ! = NULL & &
asprintf ( & options [ nr_options ] , " Zoom %s %s(%d) thread " ,
2010-04-05 12:02:18 -03:00
( thread_filter ? " out of " : " into " ) ,
( thread - > comm_set ? thread - > comm : " " ) ,
thread - > pid ) > 0 )
2010-04-03 22:44:37 -03:00
zoom_thread = nr_options + + ;
2010-04-05 12:02:18 -03:00
dso = browser - > selection - > map ? browser - > selection - > map - > dso : NULL ;
if ( dso ! = NULL & &
asprintf ( & options [ nr_options ] , " Zoom %s %s DSO " ,
( dso_filter ? " out of " : " into " ) ,
( dso - > kernel ? " the Kernel " : dso - > short_name ) ) > 0 )
zoom_dso = nr_options + + ;
2010-04-03 16:30:44 -03:00
options [ nr_options + + ] = ( char * ) " Exit " ;
2010-03-24 16:40:14 -03:00
choice = popup_menu ( nr_options , options ) ;
2010-04-03 16:30:44 -03:00
for ( i = 0 ; i < nr_options - 1 ; + + i )
free ( options [ i ] ) ;
2010-03-24 16:40:14 -03:00
if ( choice = = nr_options - 1 )
2010-03-11 20:12:44 -03:00
break ;
2010-04-03 22:44:37 -03:00
if ( choice = = - 1 )
continue ;
2010-03-24 16:40:19 -03:00
do_annotate :
2010-04-03 16:30:44 -03:00
if ( choice = = annotate ) {
2010-05-11 23:18:06 -03:00
struct hist_entry * he ;
2010-04-03 11:25:56 -03:00
if ( browser - > selection - > map - > dso - > origin = = DSO__ORIG_KERNEL ) {
2010-05-11 18:01:23 -03:00
ui_helpline__puts ( " No vmlinux file found, can't "
2010-03-24 16:40:19 -03:00
" annotate with just a "
" kallsyms file " ) ;
continue ;
}
2010-05-11 23:18:06 -03:00
he = hist_browser__selected_entry ( browser ) ;
if ( he = = NULL )
continue ;
hist_entry__annotate_browser ( he ) ;
2010-04-03 22:44:37 -03:00
} else if ( choice = = zoom_dso ) {
2010-04-05 12:02:18 -03:00
if ( dso_filter ) {
2010-05-11 18:01:23 -03:00
ui_helpline__pop ( ) ;
2010-04-05 12:02:18 -03:00
dso_filter = NULL ;
} else {
2010-05-11 18:01:23 -03:00
ui_helpline__fpush ( " To zoom out press -> + \" Zoom out of %s DSO \" " ,
dso - > kernel ? " the Kernel " : dso - > short_name ) ;
2010-04-05 12:02:18 -03:00
dso_filter = dso ;
}
2010-05-11 11:10:15 -03:00
hists__filter_by_dso ( self , dso_filter ) ;
2010-04-05 12:02:18 -03:00
hist_browser__title ( msg , sizeof ( msg ) , input_name ,
dso_filter , thread_filter ) ;
2010-05-11 11:10:15 -03:00
if ( hist_browser__populate ( browser , self , msg ) < 0 )
2010-04-03 16:30:44 -03:00
goto out ;
2010-04-03 22:44:37 -03:00
} else if ( choice = = zoom_thread ) {
2010-04-05 12:02:18 -03:00
if ( thread_filter ) {
2010-05-11 18:01:23 -03:00
ui_helpline__pop ( ) ;
2010-04-05 12:02:18 -03:00
thread_filter = NULL ;
} else {
2010-05-11 18:01:23 -03:00
ui_helpline__fpush ( " To zoom out press -> + \" Zoom out of %s(%d) thread \" " ,
thread - > comm_set ? thread - > comm : " " ,
thread - > pid ) ;
2010-04-05 12:02:18 -03:00
thread_filter = thread ;
}
2010-05-11 11:10:15 -03:00
hists__filter_by_thread ( self , thread_filter ) ;
2010-04-05 12:02:18 -03:00
hist_browser__title ( msg , sizeof ( msg ) , input_name ,
dso_filter , thread_filter ) ;
2010-05-11 11:10:15 -03:00
if ( hist_browser__populate ( browser , self , msg ) < 0 )
2010-04-03 22:44:37 -03:00
goto out ;
2010-03-24 16:40:19 -03:00
}
2010-03-11 20:12:44 -03:00
}
2010-04-03 11:25:56 -03:00
err = 0 ;
out :
hist_browser__delete ( browser ) ;
return err ;
2010-03-11 20:12:44 -03:00
}
2010-05-11 23:18:06 -03:00
static struct newtPercentTreeColors {
const char * topColorFg , * topColorBg ;
const char * mediumColorFg , * mediumColorBg ;
const char * normalColorFg , * normalColorBg ;
const char * selColorFg , * selColorBg ;
const char * codeColorFg , * codeColorBg ;
} defaultPercentTreeColors = {
" red " , " lightgray " ,
" green " , " lightgray " ,
" black " , " lightgray " ,
" lightgray " , " magenta " ,
" blue " , " lightgray " ,
} ;
2010-03-11 20:12:44 -03:00
void setup_browser ( void )
{
2010-05-11 23:18:06 -03:00
struct newtPercentTreeColors * c = & defaultPercentTreeColors ;
2010-03-11 20:12:44 -03:00
if ( ! isatty ( 1 ) )
return ;
use_browser = true ;
newtInit ( ) ;
newtCls ( ) ;
2010-05-11 18:01:23 -03:00
ui_helpline__puts ( " " ) ;
2010-05-11 23:18:06 -03:00
SLtt_set_color ( HE_COLORSET_TOP , NULL , c - > topColorFg , c - > topColorBg ) ;
SLtt_set_color ( HE_COLORSET_MEDIUM , NULL , c - > mediumColorFg , c - > mediumColorBg ) ;
SLtt_set_color ( HE_COLORSET_NORMAL , NULL , c - > normalColorFg , c - > normalColorBg ) ;
SLtt_set_color ( HE_COLORSET_SELECTED , NULL , c - > selColorFg , c - > selColorBg ) ;
SLtt_set_color ( HE_COLORSET_CODE , NULL , c - > codeColorFg , c - > codeColorBg ) ;
2010-03-11 20:12:44 -03:00
}
2010-03-22 13:10:25 -03:00
void exit_browser ( bool wait_for_ok )
2010-03-11 20:12:44 -03:00
{
2010-03-22 13:10:25 -03:00
if ( use_browser ) {
if ( wait_for_ok ) {
char title [ ] = " Fatal Error " , ok [ ] = " Ok " ;
newtWinMessage ( title , ok , browser__last_msg ) ;
}
2010-03-11 20:12:44 -03:00
newtFinished ( ) ;
2010-03-22 13:10:25 -03:00
}
2010-03-11 20:12:44 -03:00
}