2011-02-04 14:45:46 +03:00
/*
* Copyright ( C ) 2011 , Red Hat Inc , Arnaldo Carvalho de Melo < acme @ redhat . com >
*
* Parts came from builtin - annotate . c , see those files for further
* copyright notes .
*
* Released under the GPL v2 . ( and only v2 , not any later version )
*/
# include "util.h"
# include "build-id.h"
# include "color.h"
# include "cache.h"
# include "symbol.h"
# include "debug.h"
# include "annotate.h"
2013-03-05 09:53:21 +04:00
# include "evsel.h"
2011-02-08 18:27:39 +03:00
# include <pthread.h>
2012-10-28 01:18:29 +04:00
# include <linux/bitops.h>
2011-02-04 14:45:46 +03:00
2011-09-16 01:31:41 +04:00
const char * disassembler_style ;
2012-09-04 14:32:30 +04:00
const char * objdump_path ;
2011-09-16 01:31:41 +04:00
2012-05-12 20:15:34 +04:00
static struct ins * ins__find ( const char * name ) ;
static int disasm_line__parse ( char * line , char * * namep , char * * rawp ) ;
2012-05-12 20:26:20 +04:00
static void ins__delete ( struct ins_operands * ops )
{
free ( ops - > source . raw ) ;
free ( ops - > source . name ) ;
free ( ops - > target . raw ) ;
free ( ops - > target . name ) ;
}
2012-05-08 01:54:16 +04:00
static int ins__raw_scnprintf ( struct ins * ins , char * bf , size_t size ,
struct ins_operands * ops )
{
return scnprintf ( bf , size , " %-6.6s %s " , ins - > name , ops - > raw ) ;
}
int ins__scnprintf ( struct ins * ins , char * bf , size_t size ,
struct ins_operands * ops )
{
if ( ins - > ops - > scnprintf )
return ins - > ops - > scnprintf ( ins , bf , size , ops ) ;
return ins__raw_scnprintf ( ins , bf , size , ops ) ;
}
2012-04-20 21:38:46 +04:00
static int call__parse ( struct ins_operands * ops )
2012-04-18 23:07:38 +04:00
{
2012-04-20 22:26:47 +04:00
char * endptr , * tok , * name ;
2012-04-25 15:00:23 +04:00
ops - > target . addr = strtoull ( ops - > raw , & endptr , 16 ) ;
2012-04-20 22:26:47 +04:00
name = strchr ( endptr , ' < ' ) ;
if ( name = = NULL )
goto indirect_call ;
name + + ;
tok = strchr ( name , ' > ' ) ;
if ( tok = = NULL )
return - 1 ;
* tok = ' \0 ' ;
2012-04-25 15:00:23 +04:00
ops - > target . name = strdup ( name ) ;
2012-04-20 22:26:47 +04:00
* tok = ' > ' ;
2012-04-25 15:00:23 +04:00
return ops - > target . name = = NULL ? - 1 : 0 ;
2012-04-20 22:26:47 +04:00
indirect_call :
2012-05-11 19:28:55 +04:00
tok = strchr ( endptr , ' ( ' ) ;
if ( tok ! = NULL ) {
ops - > target . addr = 0 ;
return 0 ;
}
2012-04-20 22:26:47 +04:00
tok = strchr ( endptr , ' * ' ) ;
if ( tok = = NULL )
return - 1 ;
2012-04-25 15:00:23 +04:00
ops - > target . addr = strtoull ( tok + 1 , NULL , 16 ) ;
2012-04-18 23:07:38 +04:00
return 0 ;
}
2012-04-20 22:26:47 +04:00
static int call__scnprintf ( struct ins * ins , char * bf , size_t size ,
2012-05-08 01:54:16 +04:00
struct ins_operands * ops )
2012-04-20 22:26:47 +04:00
{
2012-04-25 15:00:23 +04:00
if ( ops - > target . name )
return scnprintf ( bf , size , " %-6.6s %s " , ins - > name , ops - > target . name ) ;
2012-04-20 22:26:47 +04:00
2012-05-11 19:28:55 +04:00
if ( ops - > target . addr = = 0 )
return ins__raw_scnprintf ( ins , bf , size , ops ) ;
2012-04-25 15:00:23 +04:00
return scnprintf ( bf , size , " %-6.6s *% " PRIx64 , ins - > name , ops - > target . addr ) ;
2012-04-20 22:26:47 +04:00
}
2012-04-18 23:07:38 +04:00
static struct ins_ops call_ops = {
2012-04-20 22:26:47 +04:00
. parse = call__parse ,
. scnprintf = call__scnprintf ,
2012-04-18 23:07:38 +04:00
} ;
bool ins__is_call ( const struct ins * ins )
{
return ins - > ops = = & call_ops ;
}
2012-04-20 21:38:46 +04:00
static int jump__parse ( struct ins_operands * ops )
2012-04-18 20:58:34 +04:00
{
2012-04-20 21:38:46 +04:00
const char * s = strchr ( ops - > raw , ' + ' ) ;
2012-04-18 20:58:34 +04:00
2012-04-25 21:16:03 +04:00
ops - > target . addr = strtoll ( ops - > raw , NULL , 16 ) ;
if ( s + + ! = NULL )
ops - > target . offset = strtoll ( s , NULL , 16 ) ;
else
ops - > target . offset = UINT64_MAX ;
2012-04-18 20:58:34 +04:00
return 0 ;
}
2012-04-20 21:38:46 +04:00
static int jump__scnprintf ( struct ins * ins , char * bf , size_t size ,
2012-05-08 01:54:16 +04:00
struct ins_operands * ops )
2012-04-19 17:16:27 +04:00
{
2012-04-25 15:00:23 +04:00
return scnprintf ( bf , size , " %-6.6s % " PRIx64 , ins - > name , ops - > target . offset ) ;
2012-04-19 17:16:27 +04:00
}
2012-04-18 20:58:34 +04:00
static struct ins_ops jump_ops = {
2012-04-20 21:38:46 +04:00
. parse = jump__parse ,
. scnprintf = jump__scnprintf ,
2012-04-18 20:58:34 +04:00
} ;
bool ins__is_jump ( const struct ins * ins )
{
return ins - > ops = = & jump_ops ;
}
2012-05-11 23:48:49 +04:00
static int comment__symbol ( char * raw , char * comment , u64 * addrp , char * * namep )
{
char * endptr , * name , * t ;
if ( strstr ( raw , " (%rip) " ) = = NULL )
return 0 ;
* addrp = strtoull ( comment , & endptr , 16 ) ;
name = strchr ( endptr , ' < ' ) ;
if ( name = = NULL )
return - 1 ;
name + + ;
t = strchr ( name , ' > ' ) ;
if ( t = = NULL )
return 0 ;
* t = ' \0 ' ;
* namep = strdup ( name ) ;
* t = ' > ' ;
return 0 ;
}
2012-05-12 20:15:34 +04:00
static int lock__parse ( struct ins_operands * ops )
{
char * name ;
ops - > locked . ops = zalloc ( sizeof ( * ops - > locked . ops ) ) ;
if ( ops - > locked . ops = = NULL )
return 0 ;
if ( disasm_line__parse ( ops - > raw , & name , & ops - > locked . ops - > raw ) < 0 )
goto out_free_ops ;
2012-11-09 21:27:13 +04:00
ops - > locked . ins = ins__find ( name ) ;
if ( ops - > locked . ins = = NULL )
goto out_free_ops ;
2012-05-12 20:15:34 +04:00
2012-11-09 21:27:13 +04:00
if ( ! ops - > locked . ins - > ops )
return 0 ;
2012-05-12 20:15:34 +04:00
2012-11-09 21:27:13 +04:00
if ( ops - > locked . ins - > ops - > parse )
ops - > locked . ins - > ops - > parse ( ops - > locked . ops ) ;
2012-05-12 20:15:34 +04:00
return 0 ;
out_free_ops :
free ( ops - > locked . ops ) ;
ops - > locked . ops = NULL ;
return 0 ;
}
static int lock__scnprintf ( struct ins * ins , char * bf , size_t size ,
struct ins_operands * ops )
{
int printed ;
if ( ops - > locked . ins = = NULL )
return ins__raw_scnprintf ( ins , bf , size , ops ) ;
printed = scnprintf ( bf , size , " %-6.6s " , ins - > name ) ;
return printed + ins__scnprintf ( ops - > locked . ins , bf + printed ,
size - printed , ops - > locked . ops ) ;
}
2012-05-12 20:26:20 +04:00
static void lock__delete ( struct ins_operands * ops )
{
free ( ops - > locked . ops ) ;
free ( ops - > target . raw ) ;
free ( ops - > target . name ) ;
}
2012-05-12 20:15:34 +04:00
static struct ins_ops lock_ops = {
2012-05-12 20:26:20 +04:00
. free = lock__delete ,
2012-05-12 20:15:34 +04:00
. parse = lock__parse ,
. scnprintf = lock__scnprintf ,
} ;
2012-05-11 23:48:49 +04:00
static int mov__parse ( struct ins_operands * ops )
{
char * s = strchr ( ops - > raw , ' , ' ) , * target , * comment , prev ;
if ( s = = NULL )
return - 1 ;
* s = ' \0 ' ;
ops - > source . raw = strdup ( ops - > raw ) ;
* s = ' , ' ;
if ( ops - > source . raw = = NULL )
return - 1 ;
target = + + s ;
while ( s [ 0 ] ! = ' \0 ' & & ! isspace ( s [ 0 ] ) )
+ + s ;
prev = * s ;
* s = ' \0 ' ;
ops - > target . raw = strdup ( target ) ;
* s = prev ;
if ( ops - > target . raw = = NULL )
goto out_free_source ;
comment = strchr ( s , ' # ' ) ;
if ( comment = = NULL )
return 0 ;
while ( comment [ 0 ] ! = ' \0 ' & & isspace ( comment [ 0 ] ) )
+ + comment ;
comment__symbol ( ops - > source . raw , comment , & ops - > source . addr , & ops - > source . name ) ;
comment__symbol ( ops - > target . raw , comment , & ops - > target . addr , & ops - > target . name ) ;
return 0 ;
out_free_source :
free ( ops - > source . raw ) ;
ops - > source . raw = NULL ;
return - 1 ;
}
static int mov__scnprintf ( struct ins * ins , char * bf , size_t size ,
struct ins_operands * ops )
{
return scnprintf ( bf , size , " %-6.6s %s,%s " , ins - > name ,
ops - > source . name ? : ops - > source . raw ,
ops - > target . name ? : ops - > target . raw ) ;
}
static struct ins_ops mov_ops = {
. parse = mov__parse ,
. scnprintf = mov__scnprintf ,
} ;
2012-05-12 00:21:09 +04:00
static int dec__parse ( struct ins_operands * ops )
{
char * target , * comment , * s , prev ;
target = s = ops - > raw ;
while ( s [ 0 ] ! = ' \0 ' & & ! isspace ( s [ 0 ] ) )
+ + s ;
prev = * s ;
* s = ' \0 ' ;
ops - > target . raw = strdup ( target ) ;
* s = prev ;
if ( ops - > target . raw = = NULL )
return - 1 ;
comment = strchr ( s , ' # ' ) ;
if ( comment = = NULL )
return 0 ;
while ( comment [ 0 ] ! = ' \0 ' & & isspace ( comment [ 0 ] ) )
+ + comment ;
comment__symbol ( ops - > target . raw , comment , & ops - > target . addr , & ops - > target . name ) ;
return 0 ;
}
static int dec__scnprintf ( struct ins * ins , char * bf , size_t size ,
struct ins_operands * ops )
{
return scnprintf ( bf , size , " %-6.6s %s " , ins - > name ,
ops - > target . name ? : ops - > target . raw ) ;
}
static struct ins_ops dec_ops = {
. parse = dec__parse ,
. scnprintf = dec__scnprintf ,
} ;
2012-09-11 02:15:03 +04:00
static int nop__scnprintf ( struct ins * ins __maybe_unused , char * bf , size_t size ,
struct ins_operands * ops __maybe_unused )
2012-05-08 01:57:02 +04:00
{
return scnprintf ( bf , size , " %-6.6s " , " nop " ) ;
}
static struct ins_ops nop_ops = {
. scnprintf = nop__scnprintf ,
} ;
2012-04-18 20:58:34 +04:00
/*
* Must be sorted by name !
*/
static struct ins instructions [ ] = {
2012-05-11 23:48:49 +04:00
{ . name = " add " , . ops = & mov_ops , } ,
{ . name = " addl " , . ops = & mov_ops , } ,
{ . name = " addq " , . ops = & mov_ops , } ,
{ . name = " addw " , . ops = & mov_ops , } ,
{ . name = " and " , . ops = & mov_ops , } ,
2012-05-12 20:15:34 +04:00
{ . name = " bts " , . ops = & mov_ops , } ,
2012-04-18 23:07:38 +04:00
{ . name = " call " , . ops = & call_ops , } ,
{ . name = " callq " , . ops = & call_ops , } ,
2012-05-11 23:48:49 +04:00
{ . name = " cmp " , . ops = & mov_ops , } ,
{ . name = " cmpb " , . ops = & mov_ops , } ,
{ . name = " cmpl " , . ops = & mov_ops , } ,
{ . name = " cmpq " , . ops = & mov_ops , } ,
{ . name = " cmpw " , . ops = & mov_ops , } ,
{ . name = " cmpxch " , . ops = & mov_ops , } ,
2012-05-12 00:21:09 +04:00
{ . name = " dec " , . ops = & dec_ops , } ,
{ . name = " decl " , . ops = & dec_ops , } ,
2012-05-11 23:48:49 +04:00
{ . name = " imul " , . ops = & mov_ops , } ,
2012-05-12 00:21:09 +04:00
{ . name = " inc " , . ops = & dec_ops , } ,
{ . name = " incl " , . ops = & dec_ops , } ,
2012-04-18 20:58:34 +04:00
{ . name = " ja " , . ops = & jump_ops , } ,
2012-04-20 00:10:12 +04:00
{ . name = " jae " , . ops = & jump_ops , } ,
{ . name = " jb " , . ops = & jump_ops , } ,
{ . name = " jbe " , . ops = & jump_ops , } ,
{ . name = " jc " , . ops = & jump_ops , } ,
{ . name = " jcxz " , . ops = & jump_ops , } ,
2012-04-18 20:58:34 +04:00
{ . name = " je " , . ops = & jump_ops , } ,
2012-04-20 00:10:12 +04:00
{ . name = " jecxz " , . ops = & jump_ops , } ,
{ . name = " jg " , . ops = & jump_ops , } ,
{ . name = " jge " , . ops = & jump_ops , } ,
{ . name = " jl " , . ops = & jump_ops , } ,
{ . name = " jle " , . ops = & jump_ops , } ,
2012-04-18 20:58:34 +04:00
{ . name = " jmp " , . ops = & jump_ops , } ,
{ . name = " jmpq " , . ops = & jump_ops , } ,
2012-04-20 00:10:12 +04:00
{ . name = " jna " , . ops = & jump_ops , } ,
{ . name = " jnae " , . ops = & jump_ops , } ,
{ . name = " jnb " , . ops = & jump_ops , } ,
{ . name = " jnbe " , . ops = & jump_ops , } ,
{ . name = " jnc " , . ops = & jump_ops , } ,
2012-04-18 20:58:34 +04:00
{ . name = " jne " , . ops = & jump_ops , } ,
2012-04-20 00:10:12 +04:00
{ . name = " jng " , . ops = & jump_ops , } ,
{ . name = " jnge " , . ops = & jump_ops , } ,
{ . name = " jnl " , . ops = & jump_ops , } ,
{ . name = " jnle " , . ops = & jump_ops , } ,
{ . name = " jno " , . ops = & jump_ops , } ,
{ . name = " jnp " , . ops = & jump_ops , } ,
{ . name = " jns " , . ops = & jump_ops , } ,
{ . name = " jnz " , . ops = & jump_ops , } ,
{ . name = " jo " , . ops = & jump_ops , } ,
{ . name = " jp " , . ops = & jump_ops , } ,
{ . name = " jpe " , . ops = & jump_ops , } ,
{ . name = " jpo " , . ops = & jump_ops , } ,
{ . name = " jrcxz " , . ops = & jump_ops , } ,
2012-04-18 20:58:34 +04:00
{ . name = " js " , . ops = & jump_ops , } ,
2012-04-20 00:10:12 +04:00
{ . name = " jz " , . ops = & jump_ops , } ,
2012-05-11 23:48:49 +04:00
{ . name = " lea " , . ops = & mov_ops , } ,
2012-05-12 20:15:34 +04:00
{ . name = " lock " , . ops = & lock_ops , } ,
2012-05-11 23:48:49 +04:00
{ . name = " mov " , . ops = & mov_ops , } ,
{ . name = " movb " , . ops = & mov_ops , } ,
{ . name = " movdqa " , . ops = & mov_ops , } ,
{ . name = " movl " , . ops = & mov_ops , } ,
{ . name = " movq " , . ops = & mov_ops , } ,
{ . name = " movslq " , . ops = & mov_ops , } ,
{ . name = " movzbl " , . ops = & mov_ops , } ,
{ . name = " movzwl " , . ops = & mov_ops , } ,
2012-05-08 01:57:02 +04:00
{ . name = " nop " , . ops = & nop_ops , } ,
{ . name = " nopl " , . ops = & nop_ops , } ,
{ . name = " nopw " , . ops = & nop_ops , } ,
2012-05-11 23:48:49 +04:00
{ . name = " or " , . ops = & mov_ops , } ,
{ . name = " orl " , . ops = & mov_ops , } ,
{ . name = " test " , . ops = & mov_ops , } ,
{ . name = " testb " , . ops = & mov_ops , } ,
{ . name = " testl " , . ops = & mov_ops , } ,
2012-05-12 20:15:34 +04:00
{ . name = " xadd " , . ops = & mov_ops , } ,
2012-10-31 04:34:08 +04:00
{ . name = " xbeginl " , . ops = & jump_ops , } ,
{ . name = " xbeginq " , . ops = & jump_ops , } ,
2012-04-18 20:58:34 +04:00
} ;
static int ins__cmp ( const void * name , const void * insp )
{
const struct ins * ins = insp ;
return strcmp ( name , ins - > name ) ;
}
static struct ins * ins__find ( const char * name )
{
const int nmemb = ARRAY_SIZE ( instructions ) ;
return bsearch ( name , instructions , nmemb , sizeof ( struct ins ) , ins__cmp ) ;
}
2012-09-11 02:15:03 +04:00
int symbol__annotate_init ( struct map * map __maybe_unused , struct symbol * sym )
2011-02-04 14:45:46 +03:00
{
struct annotation * notes = symbol__annotation ( sym ) ;
2011-02-08 18:27:39 +03:00
pthread_mutex_init ( & notes - > lock , NULL ) ;
return 0 ;
}
2011-02-04 14:45:46 +03:00
2011-11-12 04:17:32 +04:00
int symbol__alloc_hist ( struct symbol * sym )
2011-02-08 18:27:39 +03:00
{
struct annotation * notes = symbol__annotation ( sym ) ;
2012-04-19 17:57:06 +04:00
const size_t size = symbol__size ( sym ) ;
2012-07-20 07:05:25 +04:00
size_t sizeof_sym_hist ;
/* Check for overflow when calculating sizeof_sym_hist */
if ( size > ( SIZE_MAX - sizeof ( struct sym_hist ) ) / sizeof ( u64 ) )
return - 1 ;
sizeof_sym_hist = ( sizeof ( struct sym_hist ) + size * sizeof ( u64 ) ) ;
/* Check for overflow in zalloc argument */
if ( sizeof_sym_hist > ( SIZE_MAX - sizeof ( * notes - > src ) )
/ symbol_conf . nr_events )
return - 1 ;
2011-02-08 18:27:39 +03:00
2011-11-12 04:17:32 +04:00
notes - > src = zalloc ( sizeof ( * notes - > src ) + symbol_conf . nr_events * sizeof_sym_hist ) ;
2011-02-08 18:27:39 +03:00
if ( notes - > src = = NULL )
return - 1 ;
notes - > src - > sizeof_sym_hist = sizeof_sym_hist ;
2011-11-12 04:17:32 +04:00
notes - > src - > nr_histograms = symbol_conf . nr_events ;
2011-02-08 18:27:39 +03:00
INIT_LIST_HEAD ( & notes - > src - > source ) ;
return 0 ;
2011-02-04 14:45:46 +03:00
}
2011-02-06 19:54:44 +03:00
void symbol__annotate_zero_histograms ( struct symbol * sym )
{
struct annotation * notes = symbol__annotation ( sym ) ;
2011-02-08 18:27:39 +03:00
pthread_mutex_lock ( & notes - > lock ) ;
if ( notes - > src ! = NULL )
memset ( notes - > src - > histograms , 0 ,
notes - > src - > nr_histograms * notes - > src - > sizeof_sym_hist ) ;
pthread_mutex_unlock ( & notes - > lock ) ;
2011-02-06 19:54:44 +03:00
}
2011-02-04 18:43:24 +03:00
int symbol__inc_addr_samples ( struct symbol * sym , struct map * map ,
int evidx , u64 addr )
2011-02-04 14:45:46 +03:00
{
2011-02-04 18:43:24 +03:00
unsigned offset ;
2011-02-04 14:45:46 +03:00
struct annotation * notes ;
struct sym_hist * h ;
notes = symbol__annotation ( sym ) ;
2011-02-08 18:27:39 +03:00
if ( notes - > src = = NULL )
2011-02-04 14:45:46 +03:00
return - ENOMEM ;
pr_debug3 ( " %s: addr=%# " PRIx64 " \n " , __func__ , map - > unmap_ip ( map , addr ) ) ;
2012-03-27 19:55:57 +04:00
if ( addr < sym - > start | | addr > sym - > end )
return - ERANGE ;
2011-02-04 14:45:46 +03:00
2011-02-04 18:43:24 +03:00
offset = addr - sym - > start ;
h = annotation__histogram ( notes , evidx ) ;
2011-02-04 14:45:46 +03:00
h - > sum + + ;
h - > addr [ offset ] + + ;
pr_debug3 ( " %# " PRIx64 " %s: period++ [addr: %# " PRIx64 " , %# " PRIx64
2011-02-04 18:43:24 +03:00
" , evidx=%d] => % " PRIu64 " \n " , sym - > start , sym - > name ,
addr , addr - sym - > start , evidx , h - > addr [ offset ] ) ;
2011-02-04 14:45:46 +03:00
return 0 ;
}
2012-04-18 20:58:34 +04:00
static void disasm_line__init_ins ( struct disasm_line * dl )
{
dl - > ins = ins__find ( dl - > name ) ;
if ( dl - > ins = = NULL )
return ;
if ( ! dl - > ins - > ops )
return ;
2012-04-20 21:38:46 +04:00
if ( dl - > ins - > ops - > parse )
dl - > ins - > ops - > parse ( & dl - > ops ) ;
2012-04-18 20:58:34 +04:00
}
2012-05-12 20:15:34 +04:00
static int disasm_line__parse ( char * line , char * * namep , char * * rawp )
{
char * name = line , tmp ;
while ( isspace ( name [ 0 ] ) )
+ + name ;
if ( name [ 0 ] = = ' \0 ' )
return - 1 ;
* rawp = name + 1 ;
while ( ( * rawp ) [ 0 ] ! = ' \0 ' & & ! isspace ( ( * rawp ) [ 0 ] ) )
+ + * rawp ;
tmp = ( * rawp ) [ 0 ] ;
( * rawp ) [ 0 ] = ' \0 ' ;
* namep = strdup ( name ) ;
if ( * namep = = NULL )
goto out_free_name ;
( * rawp ) [ 0 ] = tmp ;
if ( ( * rawp ) [ 0 ] ! = ' \0 ' ) {
( * rawp ) + + ;
while ( isspace ( ( * rawp ) [ 0 ] ) )
+ + ( * rawp ) ;
}
return 0 ;
out_free_name :
free ( * namep ) ;
* namep = NULL ;
return - 1 ;
}
2012-04-15 22:24:39 +04:00
static struct disasm_line * disasm_line__new ( s64 offset , char * line , size_t privsize )
2011-02-04 14:45:46 +03:00
{
2012-04-15 22:52:18 +04:00
struct disasm_line * dl = zalloc ( sizeof ( * dl ) + privsize ) ;
2011-02-04 14:45:46 +03:00
2012-04-15 22:24:39 +04:00
if ( dl ! = NULL ) {
dl - > offset = offset ;
dl - > line = strdup ( line ) ;
if ( dl - > line = = NULL )
2012-04-02 19:59:01 +04:00
goto out_delete ;
2012-04-15 22:52:18 +04:00
if ( offset ! = - 1 ) {
2012-05-12 20:15:34 +04:00
if ( disasm_line__parse ( dl - > line , & dl - > name , & dl - > ops . raw ) < 0 )
2012-04-15 22:52:18 +04:00
goto out_free_line ;
2012-04-18 20:58:34 +04:00
disasm_line__init_ins ( dl ) ;
2012-04-15 22:52:18 +04:00
}
2011-02-04 14:45:46 +03:00
}
2012-04-15 22:24:39 +04:00
return dl ;
2012-04-15 22:52:18 +04:00
out_free_line :
free ( dl - > line ) ;
2012-04-02 19:59:01 +04:00
out_delete :
2012-04-15 22:24:39 +04:00
free ( dl ) ;
2012-04-02 19:59:01 +04:00
return NULL ;
2011-02-04 14:45:46 +03:00
}
2012-04-15 22:24:39 +04:00
void disasm_line__free ( struct disasm_line * dl )
2011-02-04 14:45:46 +03:00
{
2012-04-15 22:24:39 +04:00
free ( dl - > line ) ;
2012-04-15 22:52:18 +04:00
free ( dl - > name ) ;
2012-05-12 20:26:20 +04:00
if ( dl - > ins & & dl - > ins - > ops - > free )
dl - > ins - > ops - > free ( & dl - > ops ) ;
else
ins__delete ( & dl - > ops ) ;
2012-04-15 22:24:39 +04:00
free ( dl ) ;
2011-02-04 14:45:46 +03:00
}
2012-05-08 01:54:16 +04:00
int disasm_line__scnprintf ( struct disasm_line * dl , char * bf , size_t size , bool raw )
{
if ( raw | | ! dl - > ins )
return scnprintf ( bf , size , " %-6.6s %s " , dl - > name , dl - > ops . raw ) ;
return ins__scnprintf ( dl - > ins , bf , size , & dl - > ops ) ;
}
2012-04-15 22:24:39 +04:00
static void disasm__add ( struct list_head * head , struct disasm_line * line )
2011-02-04 14:45:46 +03:00
{
list_add_tail ( & line - > node , head ) ;
}
2012-04-15 22:24:39 +04:00
struct disasm_line * disasm__get_next_ip_line ( struct list_head * head , struct disasm_line * pos )
2011-02-04 14:45:46 +03:00
{
list_for_each_entry_continue ( pos , head , node )
if ( pos - > offset > = 0 )
return pos ;
return NULL ;
}
2013-03-05 09:53:23 +04:00
static double disasm__calc_percent ( struct disasm_line * next ,
struct annotation * notes , int evidx ,
s64 offset , u64 len , const char * * path )
{
struct source_line * src_line = notes - > src - > lines ;
struct sym_hist * h = annotation__histogram ( notes , evidx ) ;
unsigned int hits = 0 ;
double percent = 0.0 ;
while ( offset < ( s64 ) len & &
( next = = NULL | | offset < next - > offset ) ) {
if ( src_line ) {
if ( * path = = NULL )
* path = src_line [ offset ] . path ;
percent + = src_line [ offset ] . percent ;
} else
hits + = h - > addr [ offset ] ;
+ + offset ;
}
if ( src_line = = NULL & & h - > sum )
percent = 100.0 * hits / h - > sum ;
return percent ;
}
2012-04-15 22:24:39 +04:00
static int disasm_line__print ( struct disasm_line * dl , struct symbol * sym , u64 start ,
2013-03-05 09:53:21 +04:00
struct perf_evsel * evsel , u64 len , int min_pcnt , int printed ,
2012-04-15 22:24:39 +04:00
int max_lines , struct disasm_line * queue )
2011-02-04 14:45:46 +03:00
{
static const char * prev_line ;
static const char * prev_color ;
2012-04-15 22:24:39 +04:00
if ( dl - > offset ! = - 1 ) {
2011-02-04 14:45:46 +03:00
const char * path = NULL ;
2013-03-05 09:53:23 +04:00
double percent ;
2011-02-04 14:45:46 +03:00
const char * color ;
struct annotation * notes = symbol__annotation ( sym ) ;
2012-04-15 22:24:39 +04:00
s64 offset = dl - > offset ;
2012-04-02 19:59:01 +04:00
const u64 addr = start + offset ;
2012-04-15 22:24:39 +04:00
struct disasm_line * next ;
2011-02-08 18:27:39 +03:00
2012-04-15 22:24:39 +04:00
next = disasm__get_next_ip_line ( & notes - > src - > source , dl ) ;
2011-02-04 14:45:46 +03:00
2013-03-05 09:53:23 +04:00
percent = disasm__calc_percent ( next , notes , evsel - > idx ,
offset , len , & path ) ;
2011-02-05 20:37:31 +03:00
if ( percent < min_pcnt )
2011-02-06 19:54:44 +03:00
return - 1 ;
2011-02-08 20:01:39 +03:00
if ( max_lines & & printed > = max_lines )
2011-02-06 19:54:44 +03:00
return 1 ;
2011-02-05 20:37:31 +03:00
2011-02-08 20:29:25 +03:00
if ( queue ! = NULL ) {
list_for_each_entry_from ( queue , & notes - > src - > source , node ) {
2012-04-15 22:24:39 +04:00
if ( queue = = dl )
2011-02-08 20:29:25 +03:00
break ;
2013-03-05 09:53:21 +04:00
disasm_line__print ( queue , sym , start , evsel , len ,
2011-02-08 20:29:25 +03:00
0 , 0 , 1 , NULL ) ;
}
}
2011-02-04 14:45:46 +03:00
color = get_percent_color ( percent ) ;
/*
* Also color the filename and line if needed , with
* the same color than the percentage . Don ' t print it
* twice for close colored addr with the same filename : line
*/
if ( path ) {
if ( ! prev_line | | strcmp ( prev_line , path )
| | color ! = prev_color ) {
color_fprintf ( stdout , color , " %s " , path ) ;
prev_line = path ;
prev_color = color ;
}
}
color_fprintf ( stdout , color , " %7.2f " , percent ) ;
printf ( " : " ) ;
2012-04-02 19:59:01 +04:00
color_fprintf ( stdout , PERF_COLOR_MAGENTA , " % " PRIx64 " : " , addr ) ;
2012-04-15 22:24:39 +04:00
color_fprintf ( stdout , PERF_COLOR_BLUE , " %s \n " , dl - > line ) ;
2011-02-08 20:01:39 +03:00
} else if ( max_lines & & printed > = max_lines )
2011-02-06 19:54:44 +03:00
return 1 ;
else {
2011-02-08 20:29:25 +03:00
if ( queue )
return - 1 ;
2012-04-15 22:24:39 +04:00
if ( ! * dl - > line )
2011-02-04 14:45:46 +03:00
printf ( " : \n " ) ;
else
2012-04-15 22:24:39 +04:00
printf ( " : %s \n " , dl - > line ) ;
2011-02-04 14:45:46 +03:00
}
2011-02-06 19:54:44 +03:00
return 0 ;
2011-02-04 14:45:46 +03:00
}
2013-03-05 09:53:22 +04:00
/*
* symbol__parse_objdump_line ( ) parses objdump output ( with - d - - no - show - raw )
* which looks like following
*
* 0000000000415500 < _init > :
* 415500 : sub $ 0x8 , % rsp
* 415504 : mov 0x2f5ad5 ( % rip ) , % rax # 70 afe0 < _DYNAMIC + 0x2f8 >
* 41550 b : test % rax , % rax
* 41550 e : je 415515 < _init + 0x15 >
* 415510 : callq 416e70 < __gmon_start__ @ plt >
* 415515 : add $ 0x8 , % rsp
* 415519 : retq
*
* it will be parsed and saved into struct disasm_line as
* < offset > < name > < ops . raw >
*
* The offset will be a relative offset from the start of the symbol and - 1
* means that it ' s not a disassembly line so should be treated differently .
* The ops . raw part will be parsed further according to type of the instruction .
*/
2011-02-08 18:27:39 +03:00
static int symbol__parse_objdump_line ( struct symbol * sym , struct map * map ,
FILE * file , size_t privsize )
2011-02-04 14:45:46 +03:00
{
2011-02-08 18:27:39 +03:00
struct annotation * notes = symbol__annotation ( sym ) ;
2012-04-15 22:24:39 +04:00
struct disasm_line * dl ;
2012-04-02 19:59:01 +04:00
char * line = NULL , * parsed_line , * tmp , * tmp2 , * c ;
2011-02-04 14:45:46 +03:00
size_t line_len ;
s64 line_ip , offset = - 1 ;
if ( getline ( & line , & line_len , file ) < 0 )
return - 1 ;
if ( ! line )
return - 1 ;
while ( line_len ! = 0 & & isspace ( line [ line_len - 1 ] ) )
line [ - - line_len ] = ' \0 ' ;
c = strchr ( line , ' \n ' ) ;
if ( c )
* c = 0 ;
line_ip = - 1 ;
2012-04-12 00:04:59 +04:00
parsed_line = line ;
2011-02-04 14:45:46 +03:00
/*
* Strip leading spaces :
*/
tmp = line ;
while ( * tmp ) {
if ( * tmp ! = ' ' )
break ;
tmp + + ;
}
if ( * tmp ) {
/*
* Parse hexa addresses followed by ' : '
*/
line_ip = strtoull ( tmp , & tmp2 , 16 ) ;
if ( * tmp2 ! = ' : ' | | tmp = = tmp2 | | tmp2 [ 1 ] = = ' \0 ' )
line_ip = - 1 ;
}
if ( line_ip ! = - 1 ) {
u64 start = map__rip_2objdump ( map , sym - > start ) ,
end = map__rip_2objdump ( map , sym - > end ) ;
offset = line_ip - start ;
if ( offset < 0 | | ( u64 ) line_ip > end )
offset = - 1 ;
2012-04-02 19:59:01 +04:00
else
parsed_line = tmp2 + 1 ;
2012-04-12 00:04:59 +04:00
}
2011-02-04 14:45:46 +03:00
2012-04-15 22:24:39 +04:00
dl = disasm_line__new ( offset , parsed_line , privsize ) ;
2012-04-02 19:59:01 +04:00
free ( line ) ;
2012-04-15 22:24:39 +04:00
if ( dl = = NULL )
2011-02-04 14:45:46 +03:00
return - 1 ;
2012-04-02 19:59:01 +04:00
2012-04-15 22:24:39 +04:00
disasm__add ( & notes - > src - > source , dl ) ;
2011-02-04 14:45:46 +03:00
return 0 ;
}
2011-02-08 18:27:39 +03:00
int symbol__annotate ( struct symbol * sym , struct map * map , size_t privsize )
2011-02-04 14:45:46 +03:00
{
struct dso * dso = map - > dso ;
char * filename = dso__build_id_filename ( dso , NULL , 0 ) ;
bool free_filename = true ;
char command [ PATH_MAX * 2 ] ;
FILE * file ;
int err = 0 ;
char symfs_filename [ PATH_MAX ] ;
if ( filename ) {
snprintf ( symfs_filename , sizeof ( symfs_filename ) , " %s%s " ,
symbol_conf . symfs , filename ) ;
}
if ( filename = = NULL ) {
if ( dso - > has_build_id ) {
pr_err ( " Can't annotate %s: not enough memory \n " ,
sym - > name ) ;
return - ENOMEM ;
}
goto fallback ;
} else if ( readlink ( symfs_filename , command , sizeof ( command ) ) < 0 | |
strstr ( command , " [kernel.kallsyms] " ) | |
access ( symfs_filename , R_OK ) ) {
free ( filename ) ;
fallback :
/*
* If we don ' t have build - ids or the build - id file isn ' t in the
* cache , or is just a kallsyms file , well , lets hope that this
* DSO is the same as when ' perf record ' ran .
*/
filename = dso - > long_name ;
snprintf ( symfs_filename , sizeof ( symfs_filename ) , " %s%s " ,
symbol_conf . symfs , filename ) ;
free_filename = false ;
}
2012-07-22 16:14:32 +04:00
if ( dso - > symtab_type = = DSO_BINARY_TYPE__KALLSYMS ) {
2011-02-23 17:08:59 +03:00
char bf [ BUILD_ID_SIZE * 2 + 16 ] = " with build id " ;
char * build_id_msg = NULL ;
2011-02-04 14:45:46 +03:00
if ( dso - > annotate_warned )
goto out_free_filename ;
2011-02-23 17:08:59 +03:00
if ( dso - > has_build_id ) {
build_id__sprintf ( dso - > build_id ,
sizeof ( dso - > build_id ) , bf + 15 ) ;
build_id_msg = bf ;
}
2011-02-04 14:45:46 +03:00
err = - ENOENT ;
dso - > annotate_warned = 1 ;
2011-10-26 14:00:55 +04:00
pr_err ( " Can't annotate %s: \n \n "
" No vmlinux file%s \n was found in the path. \n \n "
" Please use: \n \n "
2013-02-07 13:02:12 +04:00
" perf buildid-cache -vu vmlinux \n \n "
2011-10-26 14:00:55 +04:00
" or: \n \n "
2012-02-23 12:46:24 +04:00
" --vmlinux vmlinux \n " ,
2011-02-23 17:08:59 +03:00
sym - > name , build_id_msg ? : " " ) ;
2011-02-04 14:45:46 +03:00
goto out_free_filename ;
}
pr_debug ( " %s: filename=%s, sym=%s, start=%# " PRIx64 " , end=%# " PRIx64 " \n " , __func__ ,
filename , sym - > name , map - > unmap_ip ( map , sym - > start ) ,
map - > unmap_ip ( map , sym - > end ) ) ;
pr_debug ( " annotating [%p] %30s : [%p] %30s \n " ,
dso , dso - > long_name , sym , sym - > name ) ;
snprintf ( command , sizeof ( command ) ,
2012-09-04 14:32:30 +04:00
" %s %s%s --start-address=0x%016 " PRIx64
2011-05-17 19:32:07 +04:00
" --stop-address=0x%016 " PRIx64
" -d %s %s -C %s|grep -v %s|expand " ,
2012-09-04 14:32:30 +04:00
objdump_path ? objdump_path : " objdump " ,
2011-09-16 01:31:41 +04:00
disassembler_style ? " -M " : " " ,
disassembler_style ? disassembler_style : " " ,
2011-02-04 14:45:46 +03:00
map__rip_2objdump ( map , sym - > start ) ,
perf tools: Fix truncated annotation
I get such truncated annotation results in 'perf top':
: Disassembly of section .text: ▒
: ▒
: ffffffff810966a8 <nr_iowait_cpu>: ▒
4.94 : ffffffff810966a8: movslq %edi,%rdi ▒
3.70 : ffffffff810966ab: mov $0x13700,%rax ▒
0.00 : ffffffff810966b2: add -0x7e32cb00(,%rdi,8),%rax ▒
8.64 : ffffffff810966ba: mov 0x7e0(%rax),%eax ▒
82.72 : ffffffff810966c0: cltq ▒
Note the missing 'retq' which is there in the original function:
ffffffff810966a8 <nr_iowait_cpu>:
ffffffff810966a8: 48 63 ff movslq %edi,%rdi
ffffffff810966ab: 48 c7 c0 00 37 01 00 mov $0x13700,%rax
ffffffff810966b2: 48 03 04 fd 00 35 cd add -0x7e32cb00(,%rdi,8),%rax
ffffffff810966b9: 81
ffffffff810966ba: 8b 80 e0 07 00 00 mov 0x7e0(%rax),%eax
ffffffff810966c0: 48 98 cltq
ffffffff810966c2: c3 retq
ffffffff810966c3 <this_cpu_load>:
I'm using a fairly recent binutils:
GNU objdump version 2.21.51.0.6-2.fc16 20110118
AFAICS the bug is simply that sym->end points to the last byte
of the symbol in question - while objdump's --stop-address
expects the last byte plus 1 to disassemble the full range.
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20111223130804.GA24305@elte.hu
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-12-23 17:08:04 +04:00
map__rip_2objdump ( map , sym - > end + 1 ) ,
2011-05-17 19:32:07 +04:00
symbol_conf . annotate_asm_raw ? " " : " --no-show-raw " ,
symbol_conf . annotate_src ? " -S " : " " ,
2011-02-04 14:45:46 +03:00
symfs_filename , filename ) ;
pr_debug ( " Executing: %s \n " , command ) ;
file = popen ( command , " r " ) ;
if ( ! file )
goto out_free_filename ;
while ( ! feof ( file ) )
2011-02-08 18:27:39 +03:00
if ( symbol__parse_objdump_line ( sym , map , file , privsize ) < 0 )
2011-02-04 14:45:46 +03:00
break ;
pclose ( file ) ;
out_free_filename :
if ( free_filename )
free ( filename ) ;
return err ;
}
static void insert_source_line ( struct rb_root * root , struct source_line * src_line )
{
struct source_line * iter ;
struct rb_node * * p = & root - > rb_node ;
struct rb_node * parent = NULL ;
2012-11-09 09:58:49 +04:00
int ret ;
2011-02-04 14:45:46 +03:00
while ( * p ! = NULL ) {
parent = * p ;
iter = rb_entry ( parent , struct source_line , node ) ;
2012-11-09 09:58:49 +04:00
ret = strcmp ( iter - > path , src_line - > path ) ;
if ( ret = = 0 ) {
iter - > percent_sum + = src_line - > percent ;
return ;
}
if ( ret < 0 )
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
}
src_line - > percent_sum = src_line - > percent ;
rb_link_node ( & src_line - > node , parent , p ) ;
rb_insert_color ( & src_line - > node , root ) ;
}
static void __resort_source_line ( struct rb_root * root , struct source_line * src_line )
{
struct source_line * iter ;
struct rb_node * * p = & root - > rb_node ;
struct rb_node * parent = NULL ;
while ( * p ! = NULL ) {
parent = * p ;
iter = rb_entry ( parent , struct source_line , node ) ;
if ( src_line - > percent_sum > iter - > percent_sum )
2011-02-04 14:45:46 +03:00
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
}
rb_link_node ( & src_line - > node , parent , p ) ;
rb_insert_color ( & src_line - > node , root ) ;
}
2012-11-09 09:58:49 +04:00
static void resort_source_line ( struct rb_root * dest_root , struct rb_root * src_root )
{
struct source_line * src_line ;
struct rb_node * node ;
node = rb_first ( src_root ) ;
while ( node ) {
struct rb_node * next ;
src_line = rb_entry ( node , struct source_line , node ) ;
next = rb_next ( node ) ;
rb_erase ( node , src_root ) ;
__resort_source_line ( dest_root , src_line ) ;
node = next ;
}
}
2011-02-04 14:45:46 +03:00
static void symbol__free_source_line ( struct symbol * sym , int len )
{
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 14:45:46 +03:00
int i ;
for ( i = 0 ; i < len ; i + + )
free ( src_line [ i ] . path ) ;
free ( src_line ) ;
2011-02-08 18:27:39 +03:00
notes - > src - > lines = NULL ;
2011-02-04 14:45:46 +03:00
}
/* Get the filename:line for the colored entries */
static int symbol__get_source_line ( struct symbol * sym , struct map * map ,
2013-03-05 09:53:21 +04:00
struct perf_evsel * evsel ,
struct rb_root * root , int len ,
2011-02-04 14:45:46 +03:00
const char * filename )
{
u64 start ;
int i ;
char cmd [ PATH_MAX * 2 ] ;
struct source_line * src_line ;
struct annotation * notes = symbol__annotation ( sym ) ;
2013-03-05 09:53:21 +04:00
struct sym_hist * h = annotation__histogram ( notes , evsel - > idx ) ;
2012-11-09 09:58:49 +04:00
struct rb_root tmp_root = RB_ROOT ;
2011-02-04 14:45:46 +03:00
if ( ! h - > sum )
return 0 ;
2011-02-08 18:27:39 +03:00
src_line = notes - > src - > lines = calloc ( len , sizeof ( struct source_line ) ) ;
if ( ! notes - > src - > lines )
2011-02-04 14:45:46 +03:00
return - 1 ;
2012-03-26 00:28:12 +04:00
start = map__rip_2objdump ( map , sym - > start ) ;
2011-02-04 14:45:46 +03:00
for ( i = 0 ; i < len ; i + + ) {
char * path = NULL ;
size_t line_len ;
u64 offset ;
FILE * fp ;
src_line [ i ] . percent = 100.0 * h - > addr [ i ] / h - > sum ;
if ( src_line [ i ] . percent < = 0.5 )
continue ;
offset = start + i ;
sprintf ( cmd , " addr2line -e %s %016 " PRIx64 , filename , offset ) ;
fp = popen ( cmd , " r " ) ;
if ( ! fp )
continue ;
if ( getline ( & path , & line_len , fp ) < 0 | | ! line_len )
goto next ;
src_line [ i ] . path = malloc ( sizeof ( char ) * line_len + 1 ) ;
if ( ! src_line [ i ] . path )
goto next ;
strcpy ( src_line [ i ] . path , path ) ;
2012-11-09 09:58:49 +04:00
insert_source_line ( & tmp_root , & src_line [ i ] ) ;
2011-02-04 14:45:46 +03:00
next :
pclose ( fp ) ;
}
2012-11-09 09:58:49 +04:00
resort_source_line ( root , & tmp_root ) ;
2011-02-04 14:45:46 +03:00
return 0 ;
}
static void print_summary ( struct rb_root * root , const char * filename )
{
struct source_line * src_line ;
struct rb_node * node ;
printf ( " \n Sorted summary for file %s \n " , filename ) ;
printf ( " ---------------------------------------------- \n \n " ) ;
if ( RB_EMPTY_ROOT ( root ) ) {
printf ( " Nothing higher than %1.1f%% \n " , MIN_GREEN ) ;
return ;
}
node = rb_first ( root ) ;
while ( node ) {
double percent ;
const char * color ;
char * path ;
src_line = rb_entry ( node , struct source_line , node ) ;
2012-11-09 09:58:49 +04:00
percent = src_line - > percent_sum ;
2011-02-04 14:45:46 +03:00
color = get_percent_color ( percent ) ;
path = src_line - > path ;
color_fprintf ( stdout , color , " %7.2f %s " , percent , path ) ;
node = rb_next ( node ) ;
}
}
2013-03-05 09:53:21 +04:00
static void symbol__annotate_hits ( struct symbol * sym , struct perf_evsel * evsel )
2011-02-04 14:45:46 +03:00
{
struct annotation * notes = symbol__annotation ( sym ) ;
2013-03-05 09:53:21 +04:00
struct sym_hist * h = annotation__histogram ( notes , evsel - > idx ) ;
2012-04-19 17:57:06 +04:00
u64 len = symbol__size ( sym ) , offset ;
2011-02-04 14:45:46 +03:00
for ( offset = 0 ; offset < len ; + + offset )
if ( h - > addr [ offset ] ! = 0 )
printf ( " %* " PRIx64 " : % " PRIu64 " \n " , BITS_PER_LONG / 2 ,
sym - > start + offset , h - > addr [ offset ] ) ;
printf ( " %*s: % " PRIu64 " \n " , BITS_PER_LONG / 2 , " h->sum " , h - > sum ) ;
}
2013-03-05 09:53:21 +04:00
int symbol__annotate_printf ( struct symbol * sym , struct map * map ,
struct perf_evsel * evsel , bool full_paths ,
int min_pcnt , int max_lines , int context )
2011-02-04 14:45:46 +03:00
{
struct dso * dso = map - > dso ;
2012-09-08 19:06:50 +04:00
char * filename ;
const char * d_filename ;
2011-02-08 18:27:39 +03:00
struct annotation * notes = symbol__annotation ( sym ) ;
2012-04-15 22:24:39 +04:00
struct disasm_line * pos , * queue = NULL ;
2012-04-02 19:59:01 +04:00
u64 start = map__rip_2objdump ( map , sym - > start ) ;
2011-02-08 20:29:25 +03:00
int printed = 2 , queue_len = 0 ;
2011-02-06 19:54:44 +03:00
int more = 0 ;
2011-02-04 14:45:46 +03:00
u64 len ;
2012-09-08 19:06:50 +04:00
filename = strdup ( dso - > long_name ) ;
if ( ! filename )
return - ENOMEM ;
2011-02-04 14:45:46 +03:00
if ( full_paths )
d_filename = filename ;
else
d_filename = basename ( filename ) ;
2012-04-19 17:57:06 +04:00
len = symbol__size ( sym ) ;
2011-02-04 14:45:46 +03:00
printf ( " Percent | Source code & Disassembly of %s \n " , d_filename ) ;
printf ( " ------------------------------------------------ \n " ) ;
if ( verbose )
2013-03-05 09:53:21 +04:00
symbol__annotate_hits ( sym , evsel ) ;
2011-02-04 14:45:46 +03:00
2011-02-08 18:27:39 +03:00
list_for_each_entry ( pos , & notes - > src - > source , node ) {
2011-02-08 20:29:25 +03:00
if ( context & & queue = = NULL ) {
queue = pos ;
queue_len = 0 ;
}
2013-03-05 09:53:21 +04:00
switch ( disasm_line__print ( pos , sym , start , evsel , len ,
2012-04-02 19:59:01 +04:00
min_pcnt , printed , max_lines ,
queue ) ) {
2011-02-06 19:54:44 +03:00
case 0 :
+ + printed ;
2011-02-08 20:29:25 +03:00
if ( context ) {
printed + = queue_len ;
queue = NULL ;
queue_len = 0 ;
}
2011-02-06 19:54:44 +03:00
break ;
case 1 :
/* filtered by max_lines */
+ + more ;
2011-02-05 20:37:31 +03:00
break ;
2011-02-06 19:54:44 +03:00
case - 1 :
default :
2011-02-08 20:29:25 +03:00
/*
* Filtered by min_pcnt or non IP lines when
* context ! = 0
*/
if ( ! context )
break ;
if ( queue_len = = context )
queue = list_entry ( queue - > node . next , typeof ( * queue ) , node ) ;
else
+ + queue_len ;
2011-02-06 19:54:44 +03:00
break ;
}
}
2012-09-08 19:06:50 +04:00
free ( filename ) ;
2011-02-06 19:54:44 +03:00
return more ;
}
2011-02-05 23:51:38 +03:00
2011-02-06 19:54:44 +03:00
void symbol__annotate_zero_histogram ( struct symbol * sym , int evidx )
{
struct annotation * notes = symbol__annotation ( sym ) ;
struct sym_hist * h = annotation__histogram ( notes , evidx ) ;
2011-02-08 18:27:39 +03:00
memset ( h , 0 , notes - > src - > sizeof_sym_hist ) ;
2011-02-06 19:54:44 +03:00
}
2011-02-08 18:27:39 +03:00
void symbol__annotate_decay_histogram ( struct symbol * sym , int evidx )
2011-02-06 19:54:44 +03:00
{
struct annotation * notes = symbol__annotation ( sym ) ;
struct sym_hist * h = annotation__histogram ( notes , evidx ) ;
2012-04-19 17:57:06 +04:00
int len = symbol__size ( sym ) , offset ;
2011-02-06 19:54:44 +03:00
h - > sum = 0 ;
2012-04-05 23:15:59 +04:00
for ( offset = 0 ; offset < len ; + + offset ) {
h - > addr [ offset ] = h - > addr [ offset ] * 7 / 8 ;
h - > sum + = h - > addr [ offset ] ;
2011-02-05 23:51:38 +03:00
}
}
2012-04-15 22:24:39 +04:00
void disasm__purge ( struct list_head * head )
2011-02-05 23:51:38 +03:00
{
2012-04-15 22:24:39 +04:00
struct disasm_line * pos , * n ;
2011-02-05 23:51:38 +03:00
list_for_each_entry_safe ( pos , n , head , node ) {
list_del ( & pos - > node ) ;
2012-04-15 22:24:39 +04:00
disasm_line__free ( pos ) ;
2011-02-05 23:51:38 +03:00
}
}
2012-04-15 22:52:18 +04:00
static size_t disasm_line__fprintf ( struct disasm_line * dl , FILE * fp )
{
size_t printed ;
if ( dl - > offset = = - 1 )
return fprintf ( fp , " %s \n " , dl - > line ) ;
printed = fprintf ( fp , " %# " PRIx64 " %s " , dl - > offset , dl - > name ) ;
2012-04-20 21:38:46 +04:00
if ( dl - > ops . raw [ 0 ] ! = ' \0 ' ) {
2012-04-15 22:52:18 +04:00
printed + = fprintf ( fp , " %.*s %s \n " , 6 - ( int ) printed , " " ,
2012-04-20 21:38:46 +04:00
dl - > ops . raw ) ;
2012-04-15 22:52:18 +04:00
}
return printed + fprintf ( fp , " \n " ) ;
}
size_t disasm__fprintf ( struct list_head * head , FILE * fp )
{
struct disasm_line * pos ;
size_t printed = 0 ;
list_for_each_entry ( pos , head , node )
printed + = disasm_line__fprintf ( pos , fp ) ;
return printed ;
}
2013-03-05 09:53:21 +04:00
int symbol__tty_annotate ( struct symbol * sym , struct map * map ,
struct perf_evsel * evsel , bool print_lines ,
bool full_paths , int min_pcnt , int max_lines )
2011-02-05 23:51:38 +03:00
{
struct dso * dso = map - > dso ;
const char * filename = dso - > long_name ;
struct rb_root source_line = RB_ROOT ;
u64 len ;
2011-02-08 18:27:39 +03:00
if ( symbol__annotate ( sym , map , 0 ) < 0 )
2011-02-05 23:51:38 +03:00
return - 1 ;
2012-04-19 17:57:06 +04:00
len = symbol__size ( sym ) ;
2011-02-05 23:51:38 +03:00
if ( print_lines ) {
2013-03-05 09:53:21 +04:00
symbol__get_source_line ( sym , map , evsel , & source_line ,
2011-02-05 23:51:38 +03:00
len , filename ) ;
print_summary ( & source_line , filename ) ;
2011-02-04 14:45:46 +03:00
}
2013-03-05 09:53:21 +04:00
symbol__annotate_printf ( sym , map , evsel , full_paths ,
2011-02-08 20:29:25 +03:00
min_pcnt , max_lines , 0 ) ;
2011-02-04 14:45:46 +03:00
if ( print_lines )
symbol__free_source_line ( sym , len ) ;
2012-04-15 22:24:39 +04:00
disasm__purge ( & symbol__annotation ( sym ) - > src - > source ) ;
2011-02-05 23:51:38 +03:00
2011-02-04 14:45:46 +03:00
return 0 ;
}