2011-10-26 14:00:55 +04:00
# include "../../util.h"
2010-08-10 21:54:09 +04:00
# include "../browser.h"
# include "../helpline.h"
# include "../libslang.h"
2011-10-26 14:00:55 +04:00
# include "../ui.h"
# include "../util.h"
2011-02-04 14:45:46 +03:00
# include "../../annotate.h"
2010-08-10 21:54:09 +04:00
# include "../../hist.h"
# include "../../sort.h"
# include "../../symbol.h"
2011-02-22 18:02:07 +03:00
# include <pthread.h>
2011-10-20 22:59:15 +04:00
# include <newt.h>
2010-08-10 21:54:09 +04:00
2010-08-09 22:30:40 +04:00
struct annotate_browser {
struct ui_browser b ;
struct rb_root entries ;
2010-08-10 22:14:53 +04:00
struct rb_node * curr_hot ;
2011-10-06 02:35:54 +04:00
struct objdump_line * selection ;
2011-10-14 19:31:21 +04:00
int nr_asm_entries ;
int nr_entries ;
bool hide_src_code ;
2010-08-09 22:30:40 +04:00
} ;
struct objdump_line_rb_node {
struct rb_node rb_node ;
double percent ;
u32 idx ;
2011-10-14 19:31:21 +04:00
int idx_asm ;
2010-08-09 22:30:40 +04:00
} ;
static inline
struct objdump_line_rb_node * objdump_line__rb ( struct objdump_line * self )
{
return ( struct objdump_line_rb_node * ) ( self + 1 ) ;
}
2011-10-14 19:31:21 +04:00
static bool objdump_line__filter ( struct ui_browser * browser , void * entry )
{
struct annotate_browser * ab = container_of ( browser , struct annotate_browser , b ) ;
if ( ab - > hide_src_code ) {
struct objdump_line * ol = list_entry ( entry , struct objdump_line , node ) ;
return ol - > offset = = - 1 ;
}
return false ;
}
2010-08-10 21:54:09 +04:00
static void annotate_browser__write ( struct ui_browser * self , void * entry , int row )
{
2011-10-06 02:35:54 +04:00
struct annotate_browser * ab = container_of ( self , struct annotate_browser , b ) ;
2011-10-14 19:31:21 +04:00
struct objdump_line * ol = list_entry ( entry , struct objdump_line , node ) ;
2010-08-10 21:54:09 +04:00
bool current_entry = ui_browser__is_current_entry ( self , row ) ;
int width = self - > width ;
if ( ol - > offset ! = - 1 ) {
2010-08-09 22:30:40 +04:00
struct objdump_line_rb_node * olrb = objdump_line__rb ( ol ) ;
2010-08-11 21:51:47 +04:00
ui_browser__set_percent_color ( self , olrb - > percent , current_entry ) ;
2010-08-09 22:30:40 +04:00
slsmg_printf ( " %7.2f " , olrb - > percent ) ;
} else {
2010-08-11 21:51:47 +04:00
ui_browser__set_percent_color ( self , 0 , current_entry ) ;
2010-08-09 22:30:40 +04:00
slsmg_write_nstring ( " " , 9 ) ;
}
SLsmg_write_char ( ' : ' ) ;
slsmg_write_nstring ( " " , 8 ) ;
2011-10-18 20:31:35 +04:00
/* The scroll bar isn't being used */
if ( ! self - > navkeypressed )
width + = 1 ;
2012-02-23 12:46:20 +04:00
if ( ! ab - > hide_src_code & & ol - > offset ! = - 1 )
if ( ! current_entry | | ( self - > use_navkeypressed & &
! self - > navkeypressed ) )
ui_browser__set_color ( self , HE_COLORSET_CODE ) ;
2010-08-09 22:30:40 +04:00
if ( ! * ol - > line )
slsmg_write_nstring ( " " , width - 18 ) ;
else
slsmg_write_nstring ( ol - > line , width - 18 ) ;
2011-02-09 18:59:14 +03:00
2012-02-23 12:46:20 +04:00
if ( current_entry )
2011-10-06 02:35:54 +04:00
ab - > selection = ol ;
2010-08-09 22:30:40 +04:00
}
static double objdump_line__calc_percent ( struct objdump_line * self ,
2011-02-04 18:43:24 +03:00
struct symbol * sym , int evidx )
2010-08-09 22:30:40 +04:00
{
double percent = 0.0 ;
if ( self - > offset ! = - 1 ) {
int len = sym - > end - sym - > start ;
2010-08-10 21:54:09 +04:00
unsigned int hits = 0 ;
2011-02-04 14:45:46 +03:00
struct annotation * notes = symbol__annotation ( sym ) ;
2011-02-08 18:27:39 +03:00
struct source_line * src_line = notes - > src - > lines ;
2011-02-04 18:43:24 +03:00
struct sym_hist * h = annotation__histogram ( notes , evidx ) ;
2010-08-09 22:30:40 +04:00
s64 offset = self - > offset ;
2011-02-08 18:27:39 +03:00
struct objdump_line * next ;
2010-08-09 22:30:40 +04:00
2011-02-08 18:27:39 +03:00
next = objdump__get_next_ip_line ( & notes - > src - > source , self ) ;
2010-08-10 21:54:09 +04:00
while ( offset < ( s64 ) len & &
( next = = NULL | | offset < next - > offset ) ) {
2011-02-04 14:45:46 +03:00
if ( src_line ) {
percent + = src_line [ offset ] . percent ;
2010-08-10 21:54:09 +04:00
} else
2011-02-04 14:45:46 +03:00
hits + = h - > addr [ offset ] ;
2010-08-10 21:54:09 +04:00
+ + offset ;
}
2011-02-04 14:45:46 +03:00
/*
* If the percentage wasn ' t already calculated in
* symbol__get_source_line , do it now :
*/
if ( src_line = = NULL & & h - > sum )
2010-08-10 21:54:09 +04:00
percent = 100.0 * hits / h - > sum ;
}
2010-08-09 22:30:40 +04:00
return percent ;
}
static void objdump__insert_line ( struct rb_root * self ,
struct objdump_line_rb_node * line )
{
struct rb_node * * p = & self - > rb_node ;
struct rb_node * parent = NULL ;
struct objdump_line_rb_node * l ;
while ( * p ! = NULL ) {
parent = * p ;
l = rb_entry ( parent , struct objdump_line_rb_node , rb_node ) ;
if ( line - > percent < l - > percent )
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
}
rb_link_node ( & line - > rb_node , parent , p ) ;
rb_insert_color ( & line - > rb_node , self ) ;
2010-08-10 21:54:09 +04:00
}
2010-08-10 22:14:53 +04:00
static void annotate_browser__set_top ( struct annotate_browser * self ,
struct rb_node * nd )
{
struct objdump_line_rb_node * rbpos ;
struct objdump_line * pos ;
unsigned back ;
ui_browser__refresh_dimensions ( & self - > b ) ;
back = self - > b . height / 2 ;
rbpos = rb_entry ( nd , struct objdump_line_rb_node , rb_node ) ;
pos = ( ( struct objdump_line * ) rbpos ) - 1 ;
self - > b . top_idx = self - > b . index = rbpos - > idx ;
while ( self - > b . top_idx ! = 0 & & back ! = 0 ) {
pos = list_entry ( pos - > node . prev , struct objdump_line , node ) ;
- - self - > b . top_idx ;
- - back ;
}
self - > b . top = pos ;
self - > curr_hot = nd ;
}
2011-02-22 18:02:07 +03:00
static void annotate_browser__calc_percent ( struct annotate_browser * browser ,
int evidx )
2010-08-10 22:14:53 +04:00
{
2011-10-06 02:35:54 +04:00
struct map_symbol * ms = browser - > b . priv ;
struct symbol * sym = ms - > sym ;
2011-02-22 18:02:07 +03:00
struct annotation * notes = symbol__annotation ( sym ) ;
struct objdump_line * pos ;
browser - > entries = RB_ROOT ;
pthread_mutex_lock ( & notes - > lock ) ;
list_for_each_entry ( pos , & notes - > src - > source , node ) {
struct objdump_line_rb_node * rbpos = objdump_line__rb ( pos ) ;
rbpos - > percent = objdump_line__calc_percent ( pos , sym , evidx ) ;
if ( rbpos - > percent < 0.01 ) {
RB_CLEAR_NODE ( & rbpos - > rb_node ) ;
continue ;
}
objdump__insert_line ( & browser - > entries , rbpos ) ;
}
pthread_mutex_unlock ( & notes - > lock ) ;
browser - > curr_hot = rb_last ( & browser - > entries ) ;
}
2011-10-14 19:31:21 +04:00
static bool annotate_browser__toggle_source ( struct annotate_browser * browser )
{
struct objdump_line * ol ;
struct objdump_line_rb_node * olrb ;
off_t offset = browser - > b . index - browser - > b . top_idx ;
browser - > b . seek ( & browser - > b , offset , SEEK_CUR ) ;
ol = list_entry ( browser - > b . top , struct objdump_line , node ) ;
olrb = objdump_line__rb ( ol ) ;
if ( browser - > hide_src_code ) {
if ( olrb - > idx_asm < offset )
offset = olrb - > idx ;
browser - > b . nr_entries = browser - > nr_entries ;
browser - > hide_src_code = false ;
browser - > b . seek ( & browser - > b , - offset , SEEK_CUR ) ;
browser - > b . top_idx = olrb - > idx - offset ;
browser - > b . index = olrb - > idx ;
} else {
if ( olrb - > idx_asm < 0 ) {
ui_helpline__puts ( " Only available for assembly lines. " ) ;
browser - > b . seek ( & browser - > b , - offset , SEEK_CUR ) ;
return false ;
}
if ( olrb - > idx_asm < offset )
offset = olrb - > idx_asm ;
browser - > b . nr_entries = browser - > nr_asm_entries ;
browser - > hide_src_code = true ;
browser - > b . seek ( & browser - > b , - offset , SEEK_CUR ) ;
browser - > b . top_idx = olrb - > idx_asm - offset ;
browser - > b . index = olrb - > idx_asm ;
}
return true ;
}
2011-02-22 18:02:07 +03:00
static int annotate_browser__run ( struct annotate_browser * self , int evidx ,
2011-11-12 04:17:32 +04:00
void ( * timer ) ( void * arg ) ,
2011-10-06 02:35:54 +04:00
void * arg , int delay_secs )
2011-02-22 18:02:07 +03:00
{
struct rb_node * nd = NULL ;
2011-10-06 02:35:54 +04:00
struct map_symbol * ms = self - > b . priv ;
struct symbol * sym = ms - > sym ;
2012-02-23 12:46:23 +04:00
const char * help = " <-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
" H: Go to hottest line, ->/ENTER: Line action, "
" S: Toggle source code view " ;
2010-08-11 17:07:43 +04:00
int key ;
2010-08-10 22:14:53 +04:00
2011-10-14 19:31:21 +04:00
if ( ui_browser__show ( & self - > b , sym - > name , help ) < 0 )
2010-08-10 22:14:53 +04:00
return - 1 ;
2011-02-22 18:02:07 +03:00
annotate_browser__calc_percent ( self , evidx ) ;
if ( self - > curr_hot )
annotate_browser__set_top ( self , self - > curr_hot ) ;
2010-08-10 22:14:53 +04:00
nd = self - > curr_hot ;
2011-02-22 18:02:07 +03:00
2010-08-10 22:14:53 +04:00
while ( 1 ) {
2011-10-13 15:52:46 +04:00
key = ui_browser__run ( & self - > b , delay_secs ) ;
2010-08-10 22:14:53 +04:00
2011-10-06 02:11:32 +04:00
if ( delay_secs ! = 0 ) {
2011-02-22 18:02:07 +03:00
annotate_browser__calc_percent ( self , evidx ) ;
/*
* Current line focus got out of the list of most active
* lines , NULL it so that if TAB | UNTAB is pressed , we
* move to curr_hot ( current hottest line ) .
*/
if ( nd ! = NULL & & RB_EMPTY_NODE ( nd ) )
nd = NULL ;
}
2010-08-11 17:07:43 +04:00
switch ( key ) {
2011-10-20 22:59:15 +04:00
case K_TIMER :
2011-10-06 02:11:32 +04:00
if ( timer ! = NULL )
timer ( arg ) ;
if ( delay_secs ! = 0 )
2011-02-22 18:02:07 +03:00
symbol__annotate_decay_histogram ( sym , evidx ) ;
continue ;
2011-10-20 22:59:15 +04:00
case K_TAB :
2011-02-22 18:02:07 +03:00
if ( nd ! = NULL ) {
nd = rb_prev ( nd ) ;
if ( nd = = NULL )
nd = rb_last ( & self - > entries ) ;
} else
nd = self - > curr_hot ;
2010-08-10 22:14:53 +04:00
break ;
2011-10-20 22:59:15 +04:00
case K_UNTAB :
2011-02-22 18:02:07 +03:00
if ( nd ! = NULL )
nd = rb_next ( nd ) ;
if ( nd = = NULL )
nd = rb_first ( & self - > entries ) ;
else
nd = self - > curr_hot ;
break ;
case ' H ' :
2012-02-23 12:46:21 +04:00
case ' h ' :
2011-02-22 18:02:07 +03:00
nd = self - > curr_hot ;
2010-08-10 22:14:53 +04:00
break ;
2011-10-14 19:31:21 +04:00
case ' S ' :
2012-02-23 12:46:21 +04:00
case ' s ' :
2011-10-14 19:31:21 +04:00
if ( annotate_browser__toggle_source ( self ) )
ui_helpline__puts ( help ) ;
continue ;
2011-10-20 22:59:15 +04:00
case K_ENTER :
case K_RIGHT :
2011-10-06 16:45:29 +04:00
if ( self - > selection = = NULL ) {
ui_helpline__puts ( " Huh? No selection. Report to linux-kernel@vger.kernel.org " ) ;
continue ;
}
if ( self - > selection - > offset = = - 1 ) {
ui_helpline__puts ( " Actions are only available for assembly lines. " ) ;
continue ;
} else {
2011-10-06 02:35:54 +04:00
char * s = strstr ( self - > selection - > line , " callq " ) ;
struct annotation * notes ;
struct symbol * target ;
u64 ip ;
2011-10-06 16:45:29 +04:00
if ( s = = NULL ) {
ui_helpline__puts ( " Actions are only available for the 'callq' instruction. " ) ;
2011-10-06 02:35:54 +04:00
continue ;
2011-10-06 16:45:29 +04:00
}
2011-10-06 02:35:54 +04:00
s = strchr ( s , ' ' ) ;
2011-10-06 16:45:29 +04:00
if ( s + + = = NULL ) {
ui_helpline__puts ( " Invallid callq instruction. " ) ;
2011-10-06 02:35:54 +04:00
continue ;
2011-10-06 16:45:29 +04:00
}
2011-10-06 02:35:54 +04:00
ip = strtoull ( s , NULL , 16 ) ;
ip = ms - > map - > map_ip ( ms - > map , ip ) ;
target = map__find_symbol ( ms - > map , ip , NULL ) ;
2011-10-06 16:45:29 +04:00
if ( target = = NULL ) {
ui_helpline__puts ( " The called function was not found. " ) ;
2011-10-06 02:35:54 +04:00
continue ;
2011-10-06 16:45:29 +04:00
}
2011-10-06 02:35:54 +04:00
notes = symbol__annotation ( target ) ;
pthread_mutex_lock ( & notes - > lock ) ;
2011-11-12 04:17:32 +04:00
if ( notes - > src = = NULL & & symbol__alloc_hist ( target ) < 0 ) {
2011-10-06 02:35:54 +04:00
pthread_mutex_unlock ( & notes - > lock ) ;
ui__warning ( " Not enough memory for annotating '%s' symbol! \n " ,
target - > name ) ;
continue ;
}
pthread_mutex_unlock ( & notes - > lock ) ;
2011-11-12 04:17:32 +04:00
symbol__tui_annotate ( target , ms - > map , evidx ,
2011-10-06 02:35:54 +04:00
timer , arg , delay_secs ) ;
2012-02-23 12:46:22 +04:00
ui_browser__show_title ( & self - > b , sym - > name ) ;
2011-10-06 02:35:54 +04:00
}
2011-10-19 19:18:13 +04:00
continue ;
2011-10-20 22:59:15 +04:00
case K_LEFT :
case K_ESC :
2011-10-13 15:31:22 +04:00
case ' q ' :
case CTRL ( ' c ' ) :
2010-08-10 22:14:53 +04:00
goto out ;
2011-10-13 15:31:22 +04:00
default :
continue ;
2010-08-10 22:14:53 +04:00
}
2011-02-22 18:02:07 +03:00
if ( nd ! = NULL )
annotate_browser__set_top ( self , nd ) ;
2010-08-10 22:14:53 +04:00
}
out :
2010-08-10 22:44:20 +04:00
ui_browser__hide ( & self - > b ) ;
2010-08-11 17:07:43 +04:00
return key ;
2010-08-10 22:14:53 +04:00
}
2011-11-12 04:17:32 +04:00
int hist_entry__tui_annotate ( struct hist_entry * he , int evidx ,
2011-10-06 02:11:32 +04:00
void ( * timer ) ( void * arg ) , void * arg , int delay_secs )
2011-02-04 14:45:46 +03:00
{
2011-11-12 04:17:32 +04:00
return symbol__tui_annotate ( he - > ms . sym , he - > ms . map , evidx ,
2011-10-06 02:11:32 +04:00
timer , arg , delay_secs ) ;
2011-02-04 14:45:46 +03:00
}
2011-02-22 18:02:07 +03:00
int symbol__tui_annotate ( struct symbol * sym , struct map * map , int evidx ,
2011-11-12 04:17:32 +04:00
void ( * timer ) ( void * arg ) , void * arg ,
2011-10-06 02:35:54 +04:00
int delay_secs )
2010-08-10 21:54:09 +04:00
{
struct objdump_line * pos , * n ;
2011-04-08 10:31:26 +04:00
struct annotation * notes ;
2011-10-06 02:35:54 +04:00
struct map_symbol ms = {
. map = map ,
. sym = sym ,
} ;
2010-08-09 22:30:40 +04:00
struct annotate_browser browser = {
. b = {
. refresh = ui_browser__list_head_refresh ,
. seek = ui_browser__list_head_seek ,
. write = annotate_browser__write ,
2011-10-14 19:31:21 +04:00
. filter = objdump_line__filter ,
2011-10-06 02:35:54 +04:00
. priv = & ms ,
2011-10-18 20:31:35 +04:00
. use_navkeypressed = true ,
2010-08-09 22:30:40 +04:00
} ,
2010-08-10 21:54:09 +04:00
} ;
int ret ;
2011-02-04 14:45:46 +03:00
if ( sym = = NULL )
2010-08-10 21:54:09 +04:00
return - 1 ;
2011-02-04 14:45:46 +03:00
if ( map - > dso - > annotate_warned )
2010-08-10 21:54:09 +04:00
return - 1 ;
2011-02-22 18:02:07 +03:00
if ( symbol__annotate ( sym , map , sizeof ( struct objdump_line_rb_node ) ) < 0 ) {
2011-10-26 14:00:55 +04:00
ui__error ( " %s " , ui_helpline__last_msg ) ;
2010-08-10 21:54:09 +04:00
return - 1 ;
}
ui_helpline__push ( " Press <- or ESC to exit " ) ;
2011-04-08 10:31:26 +04:00
notes = symbol__annotation ( sym ) ;
2011-02-08 18:27:39 +03:00
list_for_each_entry ( pos , & notes - > src - > source , node ) {
2011-02-22 18:02:07 +03:00
struct objdump_line_rb_node * rbpos ;
2010-08-10 21:54:09 +04:00
size_t line_len = strlen ( pos - > line ) ;
2011-02-22 18:02:07 +03:00
2010-08-09 22:30:40 +04:00
if ( browser . b . width < line_len )
browser . b . width = line_len ;
rbpos = objdump_line__rb ( pos ) ;
2011-10-14 19:31:21 +04:00
rbpos - > idx = browser . nr_entries + + ;
if ( pos - > offset ! = - 1 )
rbpos - > idx_asm = browser . nr_asm_entries + + ;
else
rbpos - > idx_asm = - 1 ;
2010-08-09 22:30:40 +04:00
}
2011-10-14 19:31:21 +04:00
browser . b . nr_entries = browser . nr_entries ;
2011-04-08 10:31:26 +04:00
browser . b . entries = & notes - > src - > source ,
2010-08-09 22:30:40 +04:00
browser . b . width + = 18 ; /* Percentage */
2011-11-12 04:17:32 +04:00
ret = annotate_browser__run ( & browser , evidx , timer , arg , delay_secs ) ;
2011-02-08 18:27:39 +03:00
list_for_each_entry_safe ( pos , n , & notes - > src - > source , node ) {
2010-08-10 21:54:09 +04:00
list_del ( & pos - > node ) ;
objdump_line__free ( pos ) ;
}
return ret ;
}