2011-10-26 08:00:55 -02:00
# include "../../util.h"
2010-08-10 14:54:09 -03:00
# include "../browser.h"
# include "../helpline.h"
# include "../libslang.h"
2011-10-26 08:00:55 -02:00
# include "../ui.h"
# include "../util.h"
2011-02-04 09:45:46 -02:00
# include "../../annotate.h"
2010-08-10 14:54:09 -03:00
# include "../../hist.h"
# include "../../sort.h"
# include "../../symbol.h"
2011-02-22 12:02:07 -03:00
# include <pthread.h>
2011-10-20 16:59:15 -02:00
# include <newt.h>
2010-08-10 14:54:09 -03:00
2010-08-09 15:30:40 -03:00
struct annotate_browser {
struct ui_browser b ;
struct rb_root entries ;
2010-08-10 15:14:53 -03:00
struct rb_node * curr_hot ;
2011-10-05 19:35:54 -03:00
struct objdump_line * selection ;
2012-04-02 12:59:01 -03:00
u64 start ;
2011-10-14 12:31:21 -03:00
int nr_asm_entries ;
int nr_entries ;
bool hide_src_code ;
2012-04-02 13:21:55 -03:00
bool use_offset ;
2010-08-09 15:30:40 -03:00
} ;
struct objdump_line_rb_node {
struct rb_node rb_node ;
double percent ;
u32 idx ;
2011-10-14 12:31:21 -03:00
int idx_asm ;
2010-08-09 15:30:40 -03: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 12:31:21 -03: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 14:54:09 -03:00
static void annotate_browser__write ( struct ui_browser * self , void * entry , int row )
{
2011-10-05 19:35:54 -03:00
struct annotate_browser * ab = container_of ( self , struct annotate_browser , b ) ;
2011-10-14 12:31:21 -03:00
struct objdump_line * ol = list_entry ( entry , struct objdump_line , node ) ;
2010-08-10 14:54:09 -03:00
bool current_entry = ui_browser__is_current_entry ( self , row ) ;
2012-04-02 12:59:01 -03:00
bool change_color = ( ! ab - > hide_src_code & &
( ! current_entry | | ( self - > use_navkeypressed & &
! self - > navkeypressed ) ) ) ;
2010-08-10 14:54:09 -03:00
int width = self - > width ;
if ( ol - > offset ! = - 1 ) {
2010-08-09 15:30:40 -03:00
struct objdump_line_rb_node * olrb = objdump_line__rb ( ol ) ;
2010-08-11 14:51:47 -03:00
ui_browser__set_percent_color ( self , olrb - > percent , current_entry ) ;
2010-08-09 15:30:40 -03:00
slsmg_printf ( " %7.2f " , olrb - > percent ) ;
} else {
2010-08-11 14:51:47 -03:00
ui_browser__set_percent_color ( self , 0 , current_entry ) ;
2010-08-09 15:30:40 -03:00
slsmg_write_nstring ( " " , 9 ) ;
}
SLsmg_write_char ( ' : ' ) ;
slsmg_write_nstring ( " " , 8 ) ;
2011-10-18 14:31:35 -02:00
/* The scroll bar isn't being used */
if ( ! self - > navkeypressed )
width + = 1 ;
2012-04-02 12:59:01 -03:00
if ( ol - > offset ! = - 1 & & change_color )
ui_browser__set_color ( self , HE_COLORSET_CODE ) ;
2012-02-23 17:46:20 +09:00
2010-08-09 15:30:40 -03:00
if ( ! * ol - > line )
slsmg_write_nstring ( " " , width - 18 ) ;
2012-04-02 12:59:01 -03:00
else if ( ol - > offset = = - 1 )
2010-08-09 15:30:40 -03:00
slsmg_write_nstring ( ol - > line , width - 18 ) ;
2012-04-02 12:59:01 -03:00
else {
char bf [ 64 ] ;
2012-04-02 13:21:55 -03:00
u64 addr = ol - > offset ;
int printed , color = - 1 ;
2012-04-02 12:59:01 -03:00
2012-04-02 13:21:55 -03:00
if ( ! ab - > use_offset )
addr + = ab - > start ;
printed = scnprintf ( bf , sizeof ( bf ) , " % " PRIx64 " : " , addr ) ;
2012-04-02 12:59:01 -03:00
if ( change_color )
color = ui_browser__set_color ( self , HE_COLORSET_ADDR ) ;
slsmg_write_nstring ( bf , printed ) ;
if ( change_color )
ui_browser__set_color ( self , color ) ;
slsmg_write_nstring ( ol - > line , width - 18 - printed ) ;
}
2011-02-09 13:59:14 -02:00
2012-02-23 17:46:20 +09:00
if ( current_entry )
2011-10-05 19:35:54 -03:00
ab - > selection = ol ;
2010-08-09 15:30:40 -03:00
}
static double objdump_line__calc_percent ( struct objdump_line * self ,
2011-02-04 13:43:24 -02:00
struct symbol * sym , int evidx )
2010-08-09 15:30:40 -03:00
{
double percent = 0.0 ;
if ( self - > offset ! = - 1 ) {
int len = sym - > end - sym - > start ;
2010-08-10 14:54:09 -03:00
unsigned int hits = 0 ;
2011-02-04 09:45:46 -02:00
struct annotation * notes = symbol__annotation ( sym ) ;
2011-02-08 13:27:39 -02:00
struct source_line * src_line = notes - > src - > lines ;
2011-02-04 13:43:24 -02:00
struct sym_hist * h = annotation__histogram ( notes , evidx ) ;
2010-08-09 15:30:40 -03:00
s64 offset = self - > offset ;
2011-02-08 13:27:39 -02:00
struct objdump_line * next ;
2010-08-09 15:30:40 -03:00
2011-02-08 13:27:39 -02:00
next = objdump__get_next_ip_line ( & notes - > src - > source , self ) ;
2010-08-10 14:54:09 -03:00
while ( offset < ( s64 ) len & &
( next = = NULL | | offset < next - > offset ) ) {
2011-02-04 09:45:46 -02:00
if ( src_line ) {
percent + = src_line [ offset ] . percent ;
2010-08-10 14:54:09 -03:00
} else
2011-02-04 09:45:46 -02:00
hits + = h - > addr [ offset ] ;
2010-08-10 14:54:09 -03:00
+ + offset ;
}
2011-02-04 09:45:46 -02: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 14:54:09 -03:00
percent = 100.0 * hits / h - > sum ;
}
2010-08-09 15:30:40 -03: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 14:54:09 -03:00
}
2010-08-10 15:14:53 -03:00
static void annotate_browser__set_top ( struct annotate_browser * self ,
2012-04-03 15:32:45 -03:00
struct objdump_line * pos , u32 idx )
2010-08-10 15:14:53 -03:00
{
unsigned back ;
ui_browser__refresh_dimensions ( & self - > b ) ;
back = self - > b . height / 2 ;
2012-04-03 15:32:45 -03:00
self - > b . top_idx = self - > b . index = idx ;
2010-08-10 15:14:53 -03:00
while ( self - > b . top_idx ! = 0 & & back ! = 0 ) {
pos = list_entry ( pos - > node . prev , struct objdump_line , node ) ;
2012-04-03 21:35:35 -03:00
if ( objdump_line__filter ( & self - > b , & pos - > node ) )
continue ;
2010-08-10 15:14:53 -03:00
- - self - > b . top_idx ;
- - back ;
}
self - > b . top = pos ;
2012-04-03 15:32:45 -03:00
}
static void annotate_browser__set_rb_top ( struct annotate_browser * browser ,
struct rb_node * nd )
{
struct objdump_line_rb_node * rbpos ;
struct objdump_line * pos ;
rbpos = rb_entry ( nd , struct objdump_line_rb_node , rb_node ) ;
pos = ( ( struct objdump_line * ) rbpos ) - 1 ;
annotate_browser__set_top ( browser , pos , rbpos - > idx ) ;
browser - > curr_hot = nd ;
2010-08-10 15:14:53 -03:00
}
2011-02-22 12:02:07 -03:00
static void annotate_browser__calc_percent ( struct annotate_browser * browser ,
int evidx )
2010-08-10 15:14:53 -03:00
{
2011-10-05 19:35:54 -03:00
struct map_symbol * ms = browser - > b . priv ;
struct symbol * sym = ms - > sym ;
2011-02-22 12: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 12:31:21 -03: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 ;
}
2012-04-02 13:58:33 -03:00
static bool annotate_browser__callq ( struct annotate_browser * browser ,
int evidx , void ( * timer ) ( void * arg ) ,
void * arg , int delay_secs )
{
struct map_symbol * ms = browser - > b . priv ;
struct symbol * sym = ms - > sym ;
struct annotation * notes ;
struct symbol * target ;
char * s = strstr ( browser - > selection - > line , " callq " ) ;
u64 ip ;
if ( s = = NULL )
return false ;
s = strchr ( s , ' ' ) ;
if ( s + + = = NULL ) {
ui_helpline__puts ( " Invallid callq instruction. " ) ;
return true ;
}
ip = strtoull ( s , NULL , 16 ) ;
ip = ms - > map - > map_ip ( ms - > map , ip ) ;
target = map__find_symbol ( ms - > map , ip , NULL ) ;
if ( target = = NULL ) {
ui_helpline__puts ( " The called function was not found. " ) ;
return true ;
}
notes = symbol__annotation ( target ) ;
pthread_mutex_lock ( & notes - > lock ) ;
if ( notes - > src = = NULL & & symbol__alloc_hist ( target ) < 0 ) {
pthread_mutex_unlock ( & notes - > lock ) ;
ui__warning ( " Not enough memory for annotating '%s' symbol! \n " ,
target - > name ) ;
return true ;
}
pthread_mutex_unlock ( & notes - > lock ) ;
symbol__tui_annotate ( target , ms - > map , evidx , timer , arg , delay_secs ) ;
ui_browser__show_title ( & browser - > b , sym - > name ) ;
return true ;
}
2012-04-03 21:35:35 -03:00
static struct objdump_line *
annotate_browser__find_offset ( struct annotate_browser * browser ,
s64 offset , s64 * idx )
{
struct map_symbol * ms = browser - > b . priv ;
struct symbol * sym = ms - > sym ;
struct annotation * notes = symbol__annotation ( sym ) ;
struct objdump_line * pos ;
* idx = 0 ;
list_for_each_entry ( pos , & notes - > src - > source , node ) {
if ( pos - > offset = = offset )
return pos ;
if ( ! objdump_line__filter ( & browser - > b , & pos - > node ) )
+ + * idx ;
}
return NULL ;
}
static bool annotate_browser__jump ( struct annotate_browser * browser )
{
const char * jumps [ ] = { " je " , " jne " , " ja " , " jmpq " , " js " , " jmp " , NULL } ;
struct objdump_line * line ;
s64 idx , offset ;
char * s = NULL ;
int i = 0 ;
while ( jumps [ i ] ) {
s = strstr ( browser - > selection - > line , jumps [ i + + ] ) ;
if ( s )
break ;
}
if ( s = = NULL )
return false ;
s = strchr ( s , ' + ' ) ;
if ( s + + = = NULL ) {
ui_helpline__puts ( " Invallid jump instruction. " ) ;
return true ;
}
offset = strtoll ( s , NULL , 16 ) ;
line = annotate_browser__find_offset ( browser , offset , & idx ) ;
if ( line = = NULL ) {
ui_helpline__puts ( " Invallid jump offset " ) ;
return true ;
}
annotate_browser__set_top ( browser , line , idx ) ;
return true ;
}
2011-02-22 12:02:07 -03:00
static int annotate_browser__run ( struct annotate_browser * self , int evidx ,
2011-11-11 22:17:32 -02:00
void ( * timer ) ( void * arg ) ,
2011-10-05 19:35:54 -03:00
void * arg , int delay_secs )
2011-02-22 12:02:07 -03:00
{
struct rb_node * nd = NULL ;
2011-10-05 19:35:54 -03:00
struct map_symbol * ms = self - > b . priv ;
struct symbol * sym = ms - > sym ;
2012-02-23 17:46:23 +09:00
const char * help = " <-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
" H: Go to hottest line, ->/ENTER: Line action, "
2012-04-02 13:21:55 -03:00
" O: Toggle offset view, "
2012-02-23 17:46:23 +09:00
" S: Toggle source code view " ;
2010-08-11 10:07:43 -03:00
int key ;
2010-08-10 15:14:53 -03:00
2011-10-14 12:31:21 -03:00
if ( ui_browser__show ( & self - > b , sym - > name , help ) < 0 )
2010-08-10 15:14:53 -03:00
return - 1 ;
2011-02-22 12:02:07 -03:00
annotate_browser__calc_percent ( self , evidx ) ;
if ( self - > curr_hot )
2012-04-03 15:32:45 -03:00
annotate_browser__set_rb_top ( self , self - > curr_hot ) ;
2010-08-10 15:14:53 -03:00
nd = self - > curr_hot ;
2011-02-22 12:02:07 -03:00
2010-08-10 15:14:53 -03:00
while ( 1 ) {
2011-10-13 08:52:46 -03:00
key = ui_browser__run ( & self - > b , delay_secs ) ;
2010-08-10 15:14:53 -03:00
2011-10-05 19:11:32 -03:00
if ( delay_secs ! = 0 ) {
2011-02-22 12: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 10:07:43 -03:00
switch ( key ) {
2011-10-20 16:59:15 -02:00
case K_TIMER :
2011-10-05 19:11:32 -03:00
if ( timer ! = NULL )
timer ( arg ) ;
if ( delay_secs ! = 0 )
2011-02-22 12:02:07 -03:00
symbol__annotate_decay_histogram ( sym , evidx ) ;
continue ;
2011-10-20 16:59:15 -02:00
case K_TAB :
2011-02-22 12: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 15:14:53 -03:00
break ;
2011-10-20 16:59:15 -02:00
case K_UNTAB :
2011-02-22 12: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 17:46:21 +09:00
case ' h ' :
2011-02-22 12:02:07 -03:00
nd = self - > curr_hot ;
2010-08-10 15:14:53 -03:00
break ;
2011-10-14 12:31:21 -03:00
case ' S ' :
2012-02-23 17:46:21 +09:00
case ' s ' :
2011-10-14 12:31:21 -03:00
if ( annotate_browser__toggle_source ( self ) )
ui_helpline__puts ( help ) ;
continue ;
2012-04-02 13:21:55 -03:00
case ' O ' :
case ' o ' :
self - > use_offset = ! self - > use_offset ;
continue ;
2011-10-20 16:59:15 -02:00
case K_ENTER :
case K_RIGHT :
2012-04-02 13:58:33 -03:00
if ( self - > selection = = NULL )
2011-10-06 09:45:29 -03:00
ui_helpline__puts ( " Huh? No selection. Report to linux-kernel@vger.kernel.org " ) ;
2012-04-02 13:58:33 -03:00
else if ( self - > selection - > offset = = - 1 )
2011-10-06 09:45:29 -03:00
ui_helpline__puts ( " Actions are only available for assembly lines. " ) ;
2012-04-03 21:35:35 -03:00
else if ( ! ( annotate_browser__jump ( self ) | |
annotate_browser__callq ( self , evidx , timer , arg , delay_secs ) ) )
ui_helpline__puts ( " Actions are only available for the 'callq' and jump instructions. " ) ;
2011-10-19 13:18:13 -02:00
continue ;
2011-10-20 16:59:15 -02:00
case K_LEFT :
case K_ESC :
2011-10-13 08:31:22 -03:00
case ' q ' :
case CTRL ( ' c ' ) :
2010-08-10 15:14:53 -03:00
goto out ;
2011-10-13 08:31:22 -03:00
default :
continue ;
2010-08-10 15:14:53 -03:00
}
2011-02-22 12:02:07 -03:00
if ( nd ! = NULL )
2012-04-03 15:32:45 -03:00
annotate_browser__set_rb_top ( self , nd ) ;
2010-08-10 15:14:53 -03:00
}
out :
2010-08-10 15:44:20 -03:00
ui_browser__hide ( & self - > b ) ;
2010-08-11 10:07:43 -03:00
return key ;
2010-08-10 15:14:53 -03:00
}
2011-11-11 22:17:32 -02:00
int hist_entry__tui_annotate ( struct hist_entry * he , int evidx ,
2011-10-05 19:11:32 -03:00
void ( * timer ) ( void * arg ) , void * arg , int delay_secs )
2011-02-04 09:45:46 -02:00
{
2011-11-11 22:17:32 -02:00
return symbol__tui_annotate ( he - > ms . sym , he - > ms . map , evidx ,
2011-10-05 19:11:32 -03:00
timer , arg , delay_secs ) ;
2011-02-04 09:45:46 -02:00
}
2011-02-22 12:02:07 -03:00
int symbol__tui_annotate ( struct symbol * sym , struct map * map , int evidx ,
2011-11-11 22:17:32 -02:00
void ( * timer ) ( void * arg ) , void * arg ,
2011-10-05 19:35:54 -03:00
int delay_secs )
2010-08-10 14:54:09 -03:00
{
struct objdump_line * pos , * n ;
2011-04-08 14:31:26 +08:00
struct annotation * notes ;
2011-10-05 19:35:54 -03:00
struct map_symbol ms = {
. map = map ,
. sym = sym ,
} ;
2010-08-09 15:30:40 -03: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 12:31:21 -03:00
. filter = objdump_line__filter ,
2011-10-05 19:35:54 -03:00
. priv = & ms ,
2011-10-18 14:31:35 -02:00
. use_navkeypressed = true ,
2010-08-09 15:30:40 -03:00
} ,
2010-08-10 14:54:09 -03:00
} ;
int ret ;
2011-02-04 09:45:46 -02:00
if ( sym = = NULL )
2010-08-10 14:54:09 -03:00
return - 1 ;
2011-02-04 09:45:46 -02:00
if ( map - > dso - > annotate_warned )
2010-08-10 14:54:09 -03:00
return - 1 ;
2011-02-22 12:02:07 -03:00
if ( symbol__annotate ( sym , map , sizeof ( struct objdump_line_rb_node ) ) < 0 ) {
2011-10-26 08:00:55 -02:00
ui__error ( " %s " , ui_helpline__last_msg ) ;
2010-08-10 14:54:09 -03:00
return - 1 ;
}
ui_helpline__push ( " Press <- or ESC to exit " ) ;
2011-04-08 14:31:26 +08:00
notes = symbol__annotation ( sym ) ;
2012-04-02 12:59:01 -03:00
browser . start = map__rip_2objdump ( map , sym - > start ) ;
2011-04-08 14:31:26 +08:00
2011-02-08 13:27:39 -02:00
list_for_each_entry ( pos , & notes - > src - > source , node ) {
2011-02-22 12:02:07 -03:00
struct objdump_line_rb_node * rbpos ;
2010-08-10 14:54:09 -03:00
size_t line_len = strlen ( pos - > line ) ;
2011-02-22 12:02:07 -03:00
2010-08-09 15:30:40 -03:00
if ( browser . b . width < line_len )
browser . b . width = line_len ;
rbpos = objdump_line__rb ( pos ) ;
2011-10-14 12:31:21 -03: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 15:30:40 -03:00
}
2011-10-14 12:31:21 -03:00
browser . b . nr_entries = browser . nr_entries ;
2011-04-08 14:31:26 +08:00
browser . b . entries = & notes - > src - > source ,
2010-08-09 15:30:40 -03:00
browser . b . width + = 18 ; /* Percentage */
2011-11-11 22:17:32 -02:00
ret = annotate_browser__run ( & browser , evidx , timer , arg , delay_secs ) ;
2011-02-08 13:27:39 -02:00
list_for_each_entry_safe ( pos , n , & notes - > src - > source , node ) {
2010-08-10 14:54:09 -03:00
list_del ( & pos - > node ) ;
objdump_line__free ( pos ) ;
}
return ret ;
}