2009-06-03 01:37:05 +04:00
/*
* builtin - report . c
*
* Builtin report command : Analyze the perf . data input file ,
* look up and read DSOs and symbol information and display
* a histogram of results , along various sorting keys .
*/
2009-05-27 11:10:38 +04:00
# include "builtin.h"
2009-05-26 11:17:18 +04:00
2009-06-03 01:37:05 +04:00
# include "util/util.h"
2009-06-04 17:19:47 +04:00
# include "util/color.h"
2009-05-18 23:24:49 +04:00
# include "util/list.h"
2009-05-27 11:50:13 +04:00
# include "util/cache.h"
2009-05-18 23:24:49 +04:00
# include "util/rbtree.h"
2009-05-28 21:55:04 +04:00
# include "util/symbol.h"
2009-06-02 00:50:19 +04:00
# include "util/string.h"
2009-05-18 19:45:42 +04:00
2009-05-26 11:17:18 +04:00
# include "perf.h"
# include "util/parse-options.h"
# include "util/parse-events.h"
2009-05-18 19:45:42 +04:00
# define SHOW_KERNEL 1
# define SHOW_USER 2
# define SHOW_HV 4
2009-05-27 11:33:18 +04:00
static char const * input_name = " perf.data " ;
2009-05-27 22:20:23 +04:00
static char * vmlinux = NULL ;
2009-06-04 16:13:04 +04:00
static char default_sort_order [ ] = " comm,dso " ;
static char * sort_order = default_sort_order ;
2009-05-18 19:45:42 +04:00
static int input ;
static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV ;
2009-05-26 20:48:58 +04:00
static int dump_trace = 0 ;
2009-06-03 11:38:58 +04:00
# define dprintf(x...) do { if (dump_trace) printf(x); } while (0)
2009-05-27 11:10:38 +04:00
static int verbose ;
2009-05-29 20:48:59 +04:00
static int full_paths ;
2009-05-26 20:48:58 +04:00
2009-05-18 19:45:42 +04:00
static unsigned long page_size ;
static unsigned long mmap_window = 32 ;
struct ip_event {
struct perf_event_header header ;
__u64 ip ;
__u32 pid , tid ;
} ;
2009-06-04 01:14:49 +04:00
2009-05-18 19:45:42 +04:00
struct mmap_event {
struct perf_event_header header ;
__u32 pid , tid ;
__u64 start ;
__u64 len ;
__u64 pgoff ;
char filename [ PATH_MAX ] ;
} ;
2009-06-04 01:14:49 +04:00
2009-05-18 19:45:42 +04:00
struct comm_event {
struct perf_event_header header ;
2009-06-04 01:14:49 +04:00
__u32 pid , tid ;
2009-05-18 19:45:42 +04:00
char comm [ 16 ] ;
} ;
2009-06-04 18:53:49 +04:00
struct fork_event {
struct perf_event_header header ;
__u32 pid , ppid ;
} ;
2009-06-05 20:07:51 +04:00
struct period_event {
2009-05-18 19:45:42 +04:00
struct perf_event_header header ;
2009-06-05 20:07:51 +04:00
__u64 time ;
__u64 id ;
__u64 sample_period ;
} ;
typedef union event_union {
struct perf_event_header header ;
struct ip_event ip ;
struct mmap_event mmap ;
struct comm_event comm ;
struct fork_event fork ;
struct period_event period ;
2009-05-18 19:45:42 +04:00
} event_t ;
static LIST_HEAD ( dsos ) ;
static struct dso * kernel_dso ;
2009-06-05 16:04:59 +04:00
static struct dso * vdso ;
2009-05-18 19:45:42 +04:00
static void dsos__add ( struct dso * dso )
{
list_add_tail ( & dso - > node , & dsos ) ;
}
static struct dso * dsos__find ( const char * name )
{
struct dso * pos ;
list_for_each_entry ( pos , & dsos , node )
if ( strcmp ( pos - > name , name ) = = 0 )
return pos ;
return NULL ;
}
static struct dso * dsos__findnew ( const char * name )
{
struct dso * dso = dsos__find ( name ) ;
2009-05-27 15:35:35 +04:00
int nr ;
2009-05-18 19:45:42 +04:00
2009-06-02 17:34:25 +04:00
if ( dso )
return dso ;
dso = dso__new ( name , 0 ) ;
if ( ! dso )
goto out_delete_dso ;
2009-05-18 19:45:42 +04:00
2009-06-04 16:13:04 +04:00
nr = dso__load ( dso , NULL , verbose ) ;
2009-06-02 17:34:25 +04:00
if ( nr < 0 ) {
2009-06-04 16:13:04 +04:00
if ( verbose )
fprintf ( stderr , " Failed to open: %s \n " , name ) ;
2009-06-02 17:34:25 +04:00
goto out_delete_dso ;
2009-05-18 19:45:42 +04:00
}
2009-06-02 17:34:25 +04:00
if ( ! nr & & verbose ) {
fprintf ( stderr ,
" No symbols found in: %s, maybe install a debug package? \n " ,
name ) ;
}
dsos__add ( dso ) ;
2009-05-18 19:45:42 +04:00
return dso ;
out_delete_dso :
dso__delete ( dso ) ;
return NULL ;
}
2009-05-27 11:10:38 +04:00
static void dsos__fprintf ( FILE * fp )
2009-05-18 19:45:42 +04:00
{
struct dso * pos ;
list_for_each_entry ( pos , & dsos , node )
dso__fprintf ( pos , fp ) ;
}
2009-06-05 16:04:59 +04:00
static struct symbol * vdso__find_symbol ( struct dso * dso , uint64_t ip )
{
return dso__find_symbol ( kernel_dso , ip ) ;
}
2009-05-27 22:20:23 +04:00
static int load_kernel ( void )
{
2009-05-28 21:55:19 +04:00
int err ;
2009-05-27 22:20:23 +04:00
2009-05-28 21:55:13 +04:00
kernel_dso = dso__new ( " [kernel] " , 0 ) ;
2009-05-27 22:20:23 +04:00
if ( ! kernel_dso )
2009-05-28 21:55:04 +04:00
return - 1 ;
2009-05-27 22:20:23 +04:00
2009-06-04 16:13:04 +04:00
err = dso__load_kernel ( kernel_dso , vmlinux , NULL , verbose ) ;
2009-05-28 21:55:04 +04:00
if ( err ) {
dso__delete ( kernel_dso ) ;
kernel_dso = NULL ;
} else
dsos__add ( kernel_dso ) ;
2009-05-27 22:20:23 +04:00
2009-06-05 16:04:59 +04:00
vdso = dso__new ( " [vdso] " , 0 ) ;
if ( ! vdso )
return - 1 ;
vdso - > find_symbol = vdso__find_symbol ;
dsos__add ( vdso ) ;
2009-05-28 21:55:04 +04:00
return err ;
2009-05-27 22:20:23 +04:00
}
2009-06-04 01:14:49 +04:00
static char __cwd [ PATH_MAX ] ;
static char * cwd = __cwd ;
static int cwdlen ;
static int strcommon ( const char * pathname )
2009-05-29 20:48:59 +04:00
{
int n = 0 ;
while ( pathname [ n ] = = cwd [ n ] & & n < cwdlen )
+ + n ;
return n ;
}
2009-05-18 19:45:42 +04:00
struct map {
struct list_head node ;
uint64_t start ;
uint64_t end ;
uint64_t pgoff ;
2009-06-05 16:04:59 +04:00
uint64_t ( * map_ip ) ( struct map * , uint64_t ) ;
2009-05-18 19:45:42 +04:00
struct dso * dso ;
} ;
2009-06-05 16:04:59 +04:00
static uint64_t map__map_ip ( struct map * map , uint64_t ip )
{
return ip - map - > start + map - > pgoff ;
}
static uint64_t vdso__map_ip ( struct map * map , uint64_t ip )
{
return ip ;
}
2009-06-04 01:14:49 +04:00
static struct map * map__new ( struct mmap_event * event )
2009-05-18 19:45:42 +04:00
{
struct map * self = malloc ( sizeof ( * self ) ) ;
if ( self ! = NULL ) {
2009-05-29 20:48:59 +04:00
const char * filename = event - > filename ;
char newfilename [ PATH_MAX ] ;
if ( cwd ) {
2009-06-04 01:14:49 +04:00
int n = strcommon ( filename ) ;
2009-05-29 20:48:59 +04:00
if ( n = = cwdlen ) {
snprintf ( newfilename , sizeof ( newfilename ) ,
" .%s " , filename + n ) ;
filename = newfilename ;
}
}
2009-05-18 19:45:42 +04:00
self - > start = event - > start ;
self - > end = event - > start + event - > len ;
self - > pgoff = event - > pgoff ;
2009-05-29 20:48:59 +04:00
self - > dso = dsos__findnew ( filename ) ;
2009-05-18 19:45:42 +04:00
if ( self - > dso = = NULL )
goto out_delete ;
2009-06-05 16:04:59 +04:00
if ( self - > dso = = vdso )
self - > map_ip = vdso__map_ip ;
else
self - > map_ip = map__map_ip ;
2009-05-18 19:45:42 +04:00
}
return self ;
out_delete :
free ( self ) ;
return NULL ;
}
2009-06-04 18:53:49 +04:00
static struct map * map__clone ( struct map * self )
{
struct map * map = malloc ( sizeof ( * self ) ) ;
if ( ! map )
return NULL ;
memcpy ( map , self , sizeof ( * self ) ) ;
return map ;
}
static int map__overlap ( struct map * l , struct map * r )
{
if ( l - > start > r - > start ) {
struct map * t = l ;
l = r ;
r = t ;
}
if ( l - > end > r - > start )
return 1 ;
return 0 ;
}
2009-05-26 23:19:04 +04:00
2009-06-04 20:54:00 +04:00
static size_t map__fprintf ( struct map * self , FILE * fp )
{
2009-06-05 07:37:35 +04:00
return fprintf ( fp , " % " PRIx64 " -% " PRIx64 " % " PRIx64 " %s \n " ,
2009-06-04 20:54:00 +04:00
self - > start , self - > end , self - > pgoff , self - > dso - > name ) ;
}
2009-05-18 19:45:42 +04:00
struct thread {
2009-05-19 16:30:23 +04:00
struct rb_node rb_node ;
2009-05-18 19:45:42 +04:00
struct list_head maps ;
pid_t pid ;
char * comm ;
} ;
static struct thread * thread__new ( pid_t pid )
{
struct thread * self = malloc ( sizeof ( * self ) ) ;
if ( self ! = NULL ) {
self - > pid = pid ;
2009-06-03 14:37:36 +04:00
self - > comm = malloc ( 32 ) ;
2009-06-03 01:24:45 +04:00
if ( self - > comm )
2009-06-03 14:37:36 +04:00
snprintf ( self - > comm , 32 , " :%d " , self - > pid ) ;
2009-05-18 19:45:42 +04:00
INIT_LIST_HEAD ( & self - > maps ) ;
}
return self ;
}
static int thread__set_comm ( struct thread * self , const char * comm )
{
2009-06-03 14:37:36 +04:00
if ( self - > comm )
free ( self - > comm ) ;
2009-05-18 19:45:42 +04:00
self - > comm = strdup ( comm ) ;
return self - > comm ? 0 : - ENOMEM ;
}
2009-06-04 20:54:00 +04:00
static size_t thread__fprintf ( struct thread * self , FILE * fp )
{
struct map * pos ;
size_t ret = fprintf ( fp , " Thread %d %s \n " , self - > pid , self - > comm ) ;
list_for_each_entry ( pos , & self - > maps , node )
ret + = map__fprintf ( pos , fp ) ;
return ret ;
}
2009-05-27 11:10:38 +04:00
static struct rb_root threads ;
2009-06-03 21:59:24 +04:00
static struct thread * last_match ;
2009-05-18 19:45:42 +04:00
2009-05-19 16:30:23 +04:00
static struct thread * threads__findnew ( pid_t pid )
2009-05-18 19:45:42 +04:00
{
2009-05-19 16:30:23 +04:00
struct rb_node * * p = & threads . rb_node ;
struct rb_node * parent = NULL ;
struct thread * th ;
2009-05-18 19:45:42 +04:00
2009-06-03 21:59:24 +04:00
/*
* Font - end cache - PID lookups come in blocks ,
* so most of the time we dont have to look up
* the full rbtree :
*/
if ( last_match & & last_match - > pid = = pid )
return last_match ;
2009-05-19 16:30:23 +04:00
while ( * p ! = NULL ) {
parent = * p ;
th = rb_entry ( parent , struct thread , rb_node ) ;
2009-05-18 19:45:42 +04:00
2009-06-03 21:59:24 +04:00
if ( th - > pid = = pid ) {
last_match = th ;
2009-05-19 16:30:23 +04:00
return th ;
2009-06-03 21:59:24 +04:00
}
2009-05-18 19:45:42 +04:00
2009-05-19 16:30:23 +04:00
if ( pid < th - > pid )
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
2009-05-18 19:45:42 +04:00
}
2009-05-19 16:30:23 +04:00
th = thread__new ( pid ) ;
if ( th ! = NULL ) {
rb_link_node ( & th - > rb_node , parent , p ) ;
rb_insert_color ( & th - > rb_node , & threads ) ;
2009-06-03 21:59:24 +04:00
last_match = th ;
2009-05-19 16:30:23 +04:00
}
2009-06-03 21:59:24 +04:00
2009-05-19 16:30:23 +04:00
return th ;
2009-05-18 19:45:42 +04:00
}
static void thread__insert_map ( struct thread * self , struct map * map )
{
2009-06-04 18:53:49 +04:00
struct map * pos , * tmp ;
list_for_each_entry_safe ( pos , tmp , & self - > maps , node ) {
if ( map__overlap ( pos , map ) ) {
list_del_init ( & pos - > node ) ;
/* XXX leaks dsos */
free ( pos ) ;
}
}
2009-05-18 19:45:42 +04:00
list_add_tail ( & map - > node , & self - > maps ) ;
}
2009-06-04 18:53:49 +04:00
static int thread__fork ( struct thread * self , struct thread * parent )
{
struct map * map ;
if ( self - > comm )
free ( self - > comm ) ;
self - > comm = strdup ( parent - > comm ) ;
if ( ! self - > comm )
return - ENOMEM ;
list_for_each_entry ( map , & parent - > maps , node ) {
struct map * new = map__clone ( map ) ;
if ( ! new )
return - ENOMEM ;
thread__insert_map ( self , new ) ;
}
return 0 ;
}
2009-05-18 19:45:42 +04:00
static struct map * thread__find_map ( struct thread * self , uint64_t ip )
{
2009-05-27 11:10:38 +04:00
struct map * pos ;
2009-05-18 19:45:42 +04:00
if ( self = = NULL )
return NULL ;
list_for_each_entry ( pos , & self - > maps , node )
if ( ip > = pos - > start & & ip < = pos - > end )
return pos ;
return NULL ;
}
2009-06-04 20:54:00 +04:00
static size_t threads__fprintf ( FILE * fp )
{
size_t ret = 0 ;
struct rb_node * nd ;
for ( nd = rb_first ( & threads ) ; nd ; nd = rb_next ( nd ) ) {
struct thread * pos = rb_entry ( nd , struct thread , rb_node ) ;
ret + = thread__fprintf ( pos , fp ) ;
}
return ret ;
}
2009-05-27 22:20:24 +04:00
/*
* histogram , sorted on item , collects counts
*/
static struct rb_root hist ;
struct hist_entry {
struct rb_node rb_node ;
struct thread * thread ;
struct map * map ;
struct dso * dso ;
struct symbol * sym ;
uint64_t ip ;
char level ;
uint32_t count ;
} ;
2009-05-27 22:20:25 +04:00
/*
* configurable sorting bits
*/
struct sort_entry {
struct list_head list ;
2009-05-28 13:08:33 +04:00
char * header ;
2009-05-27 22:20:25 +04:00
int64_t ( * cmp ) ( struct hist_entry * , struct hist_entry * ) ;
2009-06-03 14:37:36 +04:00
int64_t ( * collapse ) ( struct hist_entry * , struct hist_entry * ) ;
2009-05-27 22:20:25 +04:00
size_t ( * print ) ( FILE * fp , struct hist_entry * ) ;
} ;
2009-06-03 14:37:36 +04:00
/* --sort pid */
2009-05-27 22:20:24 +04:00
static int64_t
2009-05-27 22:20:25 +04:00
sort__thread_cmp ( struct hist_entry * left , struct hist_entry * right )
2009-05-27 22:20:24 +04:00
{
2009-05-27 22:20:25 +04:00
return right - > thread - > pid - left - > thread - > pid ;
}
static size_t
sort__thread_print ( FILE * fp , struct hist_entry * self )
{
2009-06-04 17:16:56 +04:00
return fprintf ( fp , " %16s:%5d " , self - > thread - > comm ? : " " , self - > thread - > pid ) ;
2009-05-27 22:20:25 +04:00
}
2009-05-27 22:20:24 +04:00
2009-05-27 22:20:25 +04:00
static struct sort_entry sort_thread = {
2009-06-04 17:16:56 +04:00
. header = " Command: Pid " ,
2009-05-27 22:20:25 +04:00
. cmp = sort__thread_cmp ,
. print = sort__thread_print ,
} ;
2009-06-03 14:37:36 +04:00
/* --sort comm */
2009-05-27 22:20:27 +04:00
static int64_t
sort__comm_cmp ( struct hist_entry * left , struct hist_entry * right )
2009-06-03 14:37:36 +04:00
{
return right - > thread - > pid - left - > thread - > pid ;
}
static int64_t
sort__comm_collapse ( struct hist_entry * left , struct hist_entry * right )
2009-05-27 22:20:27 +04:00
{
char * comm_l = left - > thread - > comm ;
char * comm_r = right - > thread - > comm ;
if ( ! comm_l | | ! comm_r ) {
if ( ! comm_l & & ! comm_r )
return 0 ;
else if ( ! comm_l )
return - 1 ;
else
return 1 ;
}
return strcmp ( comm_l , comm_r ) ;
}
static size_t
sort__comm_print ( FILE * fp , struct hist_entry * self )
{
2009-06-04 17:16:56 +04:00
return fprintf ( fp , " %16s " , self - > thread - > comm ) ;
2009-05-27 22:20:27 +04:00
}
static struct sort_entry sort_comm = {
2009-06-05 16:13:18 +04:00
. header = " Command " ,
2009-06-03 14:37:36 +04:00
. cmp = sort__comm_cmp ,
. collapse = sort__comm_collapse ,
. print = sort__comm_print ,
2009-05-27 22:20:27 +04:00
} ;
2009-06-03 14:37:36 +04:00
/* --sort dso */
2009-05-27 22:20:28 +04:00
static int64_t
sort__dso_cmp ( struct hist_entry * left , struct hist_entry * right )
{
struct dso * dso_l = left - > dso ;
struct dso * dso_r = right - > dso ;
if ( ! dso_l | | ! dso_r ) {
if ( ! dso_l & & ! dso_r )
return 0 ;
else if ( ! dso_l )
return - 1 ;
else
return 1 ;
}
return strcmp ( dso_l - > name , dso_r - > name ) ;
}
static size_t
sort__dso_print ( FILE * fp , struct hist_entry * self )
{
2009-06-03 01:24:45 +04:00
if ( self - > dso )
2009-06-04 17:16:56 +04:00
return fprintf ( fp , " %-25s " , self - > dso - > name ) ;
2009-06-03 01:24:45 +04:00
2009-06-04 17:16:56 +04:00
return fprintf ( fp , " %016llx " , ( __u64 ) self - > ip ) ;
2009-05-27 22:20:28 +04:00
}
static struct sort_entry sort_dso = {
2009-06-04 17:16:56 +04:00
. header = " Shared Object " ,
2009-05-27 22:20:28 +04:00
. cmp = sort__dso_cmp ,
. print = sort__dso_print ,
} ;
2009-06-03 14:37:36 +04:00
/* --sort symbol */
2009-05-27 22:20:25 +04:00
static int64_t
sort__sym_cmp ( struct hist_entry * left , struct hist_entry * right )
{
uint64_t ip_l , ip_r ;
2009-05-27 22:20:24 +04:00
if ( left - > sym = = right - > sym )
return 0 ;
ip_l = left - > sym ? left - > sym - > start : left - > ip ;
ip_r = right - > sym ? right - > sym - > start : right - > ip ;
return ( int64_t ) ( ip_r - ip_l ) ;
}
2009-05-27 22:20:25 +04:00
static size_t
sort__sym_print ( FILE * fp , struct hist_entry * self )
{
size_t ret = 0 ;
if ( verbose )
2009-06-04 17:16:56 +04:00
ret + = fprintf ( fp , " %#018llx " , ( __u64 ) self - > ip ) ;
2009-06-03 01:24:45 +04:00
2009-06-05 16:13:18 +04:00
if ( self - > sym ) {
ret + = fprintf ( fp , " [%c] %s " ,
self - > dso = = kernel_dso ? ' k ' : ' . ' , self - > sym - > name ) ;
} else {
2009-06-04 17:16:56 +04:00
ret + = fprintf ( fp , " %#016llx " , ( __u64 ) self - > ip ) ;
2009-06-05 16:13:18 +04:00
}
2009-05-27 22:20:25 +04:00
return ret ;
}
static struct sort_entry sort_sym = {
2009-06-04 17:16:56 +04:00
. header = " Symbol " ,
2009-05-28 13:08:33 +04:00
. cmp = sort__sym_cmp ,
. print = sort__sym_print ,
2009-05-27 22:20:25 +04:00
} ;
2009-06-03 14:37:36 +04:00
static int sort__need_collapse = 0 ;
2009-05-27 22:20:26 +04:00
struct sort_dimension {
2009-06-05 16:13:18 +04:00
char * name ;
struct sort_entry * entry ;
int taken ;
2009-05-27 22:20:26 +04:00
} ;
static struct sort_dimension sort_dimensions [ ] = {
{ . name = " pid " , . entry = & sort_thread , } ,
2009-05-27 22:20:27 +04:00
{ . name = " comm " , . entry = & sort_comm , } ,
2009-05-27 22:20:28 +04:00
{ . name = " dso " , . entry = & sort_dso , } ,
2009-05-27 22:20:26 +04:00
{ . name = " symbol " , . entry = & sort_sym , } ,
} ;
2009-05-27 22:20:25 +04:00
static LIST_HEAD ( hist_entry__sort_list ) ;
2009-05-27 22:20:26 +04:00
static int sort_dimension__add ( char * tok )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( sort_dimensions ) ; i + + ) {
struct sort_dimension * sd = & sort_dimensions [ i ] ;
if ( sd - > taken )
continue ;
2009-06-03 12:07:39 +04:00
if ( strncasecmp ( tok , sd - > name , strlen ( tok ) ) )
2009-05-27 22:20:26 +04:00
continue ;
2009-06-03 14:37:36 +04:00
if ( sd - > entry - > collapse )
sort__need_collapse = 1 ;
2009-05-27 22:20:26 +04:00
list_add_tail ( & sd - > entry - > list , & hist_entry__sort_list ) ;
sd - > taken = 1 ;
2009-06-03 12:07:39 +04:00
2009-05-27 22:20:26 +04:00
return 0 ;
}
return - ESRCH ;
}
2009-05-27 22:20:25 +04:00
static int64_t
hist_entry__cmp ( struct hist_entry * left , struct hist_entry * right )
{
struct sort_entry * se ;
int64_t cmp = 0 ;
list_for_each_entry ( se , & hist_entry__sort_list , list ) {
cmp = se - > cmp ( left , right ) ;
if ( cmp )
break ;
}
return cmp ;
}
2009-06-03 14:37:36 +04:00
static int64_t
hist_entry__collapse ( struct hist_entry * left , struct hist_entry * right )
{
struct sort_entry * se ;
int64_t cmp = 0 ;
list_for_each_entry ( se , & hist_entry__sort_list , list ) {
int64_t ( * f ) ( struct hist_entry * , struct hist_entry * ) ;
f = se - > collapse ? : se - > cmp ;
cmp = f ( left , right ) ;
if ( cmp )
break ;
}
return cmp ;
}
2009-05-27 22:20:25 +04:00
static size_t
hist_entry__fprintf ( FILE * fp , struct hist_entry * self , uint64_t total_samples )
{
struct sort_entry * se ;
size_t ret ;
if ( total_samples ) {
2009-06-04 17:19:47 +04:00
double percent = self - > count * 100.0 / total_samples ;
char * color = PERF_COLOR_NORMAL ;
/*
* We color high - overhead entries in red , low - overhead
* entries in green - and keep the middle ground normal :
*/
if ( percent > = 5.0 )
color = PERF_COLOR_RED ;
if ( percent < 0.5 )
color = PERF_COLOR_GREEN ;
ret = color_fprintf ( fp , color , " %6.2f%% " ,
2009-05-27 22:20:25 +04:00
( self - > count * 100.0 ) / total_samples ) ;
} else
ret = fprintf ( fp , " %12d " , self - > count ) ;
2009-06-04 17:16:56 +04:00
list_for_each_entry ( se , & hist_entry__sort_list , list ) {
fprintf ( fp , " " ) ;
2009-05-27 22:20:25 +04:00
ret + = se - > print ( fp , self ) ;
2009-06-04 17:16:56 +04:00
}
2009-05-27 22:20:25 +04:00
ret + = fprintf ( fp , " \n " ) ;
return ret ;
}
/*
* collect histogram counts
*/
2009-05-27 22:20:24 +04:00
static int
hist_entry__add ( struct thread * thread , struct map * map , struct dso * dso ,
struct symbol * sym , uint64_t ip , char level )
2009-05-18 19:45:42 +04:00
{
2009-05-27 22:20:24 +04:00
struct rb_node * * p = & hist . rb_node ;
struct rb_node * parent = NULL ;
struct hist_entry * he ;
struct hist_entry entry = {
. thread = thread ,
. map = map ,
. dso = dso ,
. sym = sym ,
. ip = ip ,
. level = level ,
. count = 1 ,
} ;
int cmp ;
while ( * p ! = NULL ) {
parent = * p ;
he = rb_entry ( parent , struct hist_entry , rb_node ) ;
cmp = hist_entry__cmp ( & entry , he ) ;
if ( ! cmp ) {
he - > count + + ;
return 0 ;
}
if ( cmp < 0 )
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
2009-05-19 16:30:23 +04:00
}
2009-05-27 22:20:24 +04:00
he = malloc ( sizeof ( * he ) ) ;
if ( ! he )
return - ENOMEM ;
* he = entry ;
rb_link_node ( & he - > rb_node , parent , p ) ;
rb_insert_color ( & he - > rb_node , & hist ) ;
return 0 ;
2009-05-18 19:45:42 +04:00
}
2009-06-03 14:37:36 +04:00
static void hist_entry__free ( struct hist_entry * he )
{
free ( he ) ;
}
/*
* collapse the histogram
*/
static struct rb_root collapse_hists ;
static void collapse__insert_entry ( struct hist_entry * he )
{
struct rb_node * * p = & collapse_hists . rb_node ;
struct rb_node * parent = NULL ;
struct hist_entry * iter ;
int64_t cmp ;
while ( * p ! = NULL ) {
parent = * p ;
iter = rb_entry ( parent , struct hist_entry , rb_node ) ;
cmp = hist_entry__collapse ( iter , he ) ;
if ( ! cmp ) {
iter - > count + = he - > count ;
hist_entry__free ( he ) ;
return ;
}
if ( cmp < 0 )
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
}
rb_link_node ( & he - > rb_node , parent , p ) ;
rb_insert_color ( & he - > rb_node , & collapse_hists ) ;
}
static void collapse__resort ( void )
{
struct rb_node * next ;
struct hist_entry * n ;
if ( ! sort__need_collapse )
return ;
next = rb_first ( & hist ) ;
while ( next ) {
n = rb_entry ( next , struct hist_entry , rb_node ) ;
next = rb_next ( & n - > rb_node ) ;
rb_erase ( & n - > rb_node , & hist ) ;
collapse__insert_entry ( n ) ;
}
}
2009-05-27 22:20:24 +04:00
/*
* reverse the map , sort on count .
*/
static struct rb_root output_hists ;
static void output__insert_entry ( struct hist_entry * he )
2009-05-26 23:19:04 +04:00
{
2009-05-27 22:20:24 +04:00
struct rb_node * * p = & output_hists . rb_node ;
2009-05-26 23:19:04 +04:00
struct rb_node * parent = NULL ;
2009-05-27 22:20:24 +04:00
struct hist_entry * iter ;
2009-05-26 23:19:04 +04:00
while ( * p ! = NULL ) {
parent = * p ;
2009-05-27 22:20:24 +04:00
iter = rb_entry ( parent , struct hist_entry , rb_node ) ;
2009-05-26 23:19:04 +04:00
2009-05-27 22:20:24 +04:00
if ( he - > count > iter - > count )
2009-05-26 23:19:04 +04:00
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
}
2009-05-27 22:20:24 +04:00
rb_link_node ( & he - > rb_node , parent , p ) ;
rb_insert_color ( & he - > rb_node , & output_hists ) ;
2009-05-26 23:19:04 +04:00
}
2009-05-27 22:20:24 +04:00
static void output__resort ( void )
2009-05-26 23:19:04 +04:00
{
2009-06-03 14:37:36 +04:00
struct rb_node * next ;
2009-05-27 22:20:24 +04:00
struct hist_entry * n ;
2009-06-04 06:02:33 +04:00
struct rb_root * tree = & hist ;
2009-05-26 23:19:04 +04:00
2009-06-03 14:37:36 +04:00
if ( sort__need_collapse )
2009-06-04 06:02:33 +04:00
tree = & collapse_hists ;
next = rb_first ( tree ) ;
2009-06-03 14:37:36 +04:00
2009-05-27 22:20:24 +04:00
while ( next ) {
n = rb_entry ( next , struct hist_entry , rb_node ) ;
next = rb_next ( & n - > rb_node ) ;
2009-05-26 23:19:04 +04:00
2009-06-04 06:02:33 +04:00
rb_erase ( & n - > rb_node , tree ) ;
2009-05-27 22:20:24 +04:00
output__insert_entry ( n ) ;
2009-05-26 23:19:04 +04:00
}
}
2009-05-27 22:20:24 +04:00
static size_t output__fprintf ( FILE * fp , uint64_t total_samples )
2009-05-26 23:19:04 +04:00
{
2009-05-27 22:20:24 +04:00
struct hist_entry * pos ;
2009-05-27 23:36:22 +04:00
struct sort_entry * se ;
2009-05-26 23:19:04 +04:00
struct rb_node * nd ;
size_t ret = 0 ;
2009-06-04 17:16:56 +04:00
fprintf ( fp , " \n " ) ;
2009-06-04 16:21:16 +04:00
fprintf ( fp , " # \n " ) ;
2009-06-05 16:29:10 +04:00
fprintf ( fp , " # (%Ld samples) \n " , ( __u64 ) total_samples ) ;
2009-05-28 13:08:33 +04:00
fprintf ( fp , " # \n " ) ;
fprintf ( fp , " # Overhead " ) ;
list_for_each_entry ( se , & hist_entry__sort_list , list )
2009-06-04 17:16:56 +04:00
fprintf ( fp , " %s " , se - > header ) ;
2009-05-28 13:08:33 +04:00
fprintf ( fp , " \n " ) ;
fprintf ( fp , " # ........ " ) ;
2009-05-27 23:36:22 +04:00
list_for_each_entry ( se , & hist_entry__sort_list , list ) {
2009-05-28 13:08:33 +04:00
int i ;
2009-06-02 17:34:25 +04:00
fprintf ( fp , " " ) ;
2009-06-04 17:16:56 +04:00
for ( i = 0 ; i < strlen ( se - > header ) ; i + + )
2009-05-28 13:08:33 +04:00
fprintf ( fp , " . " ) ;
2009-05-27 23:36:22 +04:00
}
2009-05-28 13:08:33 +04:00
fprintf ( fp , " \n " ) ;
fprintf ( fp , " # \n " ) ;
2009-05-27 23:36:22 +04:00
2009-05-27 22:20:24 +04:00
for ( nd = rb_first ( & output_hists ) ; nd ; nd = rb_next ( nd ) ) {
pos = rb_entry ( nd , struct hist_entry , rb_node ) ;
ret + = hist_entry__fprintf ( fp , pos , total_samples ) ;
2009-05-26 23:19:04 +04:00
}
2009-06-04 16:13:04 +04:00
if ( ! strcmp ( sort_order , default_sort_order ) ) {
fprintf ( fp , " # \n " ) ;
2009-06-04 17:16:56 +04:00
fprintf ( fp , " # (For more details, try: perf report --sort comm,dso,symbol) \n " ) ;
2009-06-04 16:13:04 +04:00
fprintf ( fp , " # \n " ) ;
}
2009-06-04 17:16:56 +04:00
fprintf ( fp , " \n " ) ;
2009-06-04 16:13:04 +04:00
2009-05-26 23:19:04 +04:00
return ret ;
}
2009-06-02 23:02:36 +04:00
static void register_idle_thread ( void )
{
struct thread * thread = threads__findnew ( 0 ) ;
if ( thread = = NULL | |
thread__set_comm ( thread , " [idle] " ) ) {
fprintf ( stderr , " problem inserting idle task. \n " ) ;
exit ( - 1 ) ;
}
}
2009-06-04 18:53:49 +04:00
static unsigned long total = 0 ,
total_mmap = 0 ,
total_comm = 0 ,
total_fork = 0 ,
total_unknown = 0 ;
2009-05-27 22:20:24 +04:00
2009-06-04 01:14:49 +04:00
static int
2009-06-04 01:14:49 +04:00
process_overflow_event ( event_t * event , unsigned long offset , unsigned long head )
{
char level ;
int show = 0 ;
struct dso * dso = NULL ;
struct thread * thread = threads__findnew ( event - > ip . pid ) ;
uint64_t ip = event - > ip . ip ;
struct map * map = NULL ;
dprintf ( " %p [%p]: PERF_EVENT (IP, %d): %d: %p \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
event - > header . misc ,
event - > ip . pid ,
( void * ) ( long ) ip ) ;
dprintf ( " ... thread: %s:%d \n " , thread - > comm , thread - > pid ) ;
if ( thread = = NULL ) {
fprintf ( stderr , " problem processing %d event, skipping it. \n " ,
event - > header . type ) ;
return - 1 ;
}
2009-05-27 22:20:24 +04:00
2009-06-04 01:14:49 +04:00
if ( event - > header . misc & PERF_EVENT_MISC_KERNEL ) {
show = SHOW_KERNEL ;
level = ' k ' ;
2009-05-27 22:20:24 +04:00
2009-06-04 01:14:49 +04:00
dso = kernel_dso ;
2009-06-03 12:39:26 +04:00
2009-06-04 01:14:49 +04:00
dprintf ( " ...... dso: %s \n " , dso - > name ) ;
2009-05-27 11:10:38 +04:00
2009-06-04 01:14:49 +04:00
} else if ( event - > header . misc & PERF_EVENT_MISC_USER ) {
2009-05-27 11:10:38 +04:00
2009-06-04 01:14:49 +04:00
show = SHOW_USER ;
level = ' . ' ;
2009-05-27 22:20:24 +04:00
2009-06-04 01:14:49 +04:00
map = thread__find_map ( thread , ip ) ;
if ( map ! = NULL ) {
2009-06-05 16:04:59 +04:00
ip = map - > map_ip ( map , ip ) ;
2009-06-04 01:14:49 +04:00
dso = map - > dso ;
2009-05-18 19:45:42 +04:00
} else {
2009-06-04 01:14:49 +04:00
/*
* If this is outside of all known maps ,
* and is a negative address , try to look it
* up in the kernel dso , as it might be a
* vsyscall ( which executes in user - mode ) :
*/
if ( ( long long ) ip < 0 )
dso = kernel_dso ;
2009-05-18 19:45:42 +04:00
}
2009-06-04 01:14:49 +04:00
dprintf ( " ...... dso: %s \n " , dso ? dso - > name : " <not found> " ) ;
} else {
show = SHOW_HV ;
level = ' H ' ;
dprintf ( " ...... dso: [hypervisor] \n " ) ;
}
2009-05-18 19:45:42 +04:00
2009-06-04 01:14:49 +04:00
if ( show & show_mask ) {
2009-06-05 16:04:59 +04:00
struct symbol * sym = NULL ;
if ( dso )
sym = dso - > find_symbol ( dso , ip ) ;
2009-05-18 19:45:42 +04:00
2009-06-04 01:14:49 +04:00
if ( hist_entry__add ( thread , map , dso , sym , ip , level ) ) {
fprintf ( stderr ,
2009-05-28 00:13:17 +04:00
" problem incrementing symbol count, skipping event \n " ) ;
2009-06-04 01:14:49 +04:00
return - 1 ;
2009-05-19 16:30:23 +04:00
}
2009-05-18 19:45:42 +04:00
}
2009-06-04 01:14:49 +04:00
total + + ;
2009-05-18 19:45:42 +04:00
2009-06-04 01:14:49 +04:00
return 0 ;
}
2009-06-03 11:38:58 +04:00
2009-06-04 01:14:49 +04:00
static int
process_mmap_event ( event_t * event , unsigned long offset , unsigned long head )
{
struct thread * thread = threads__findnew ( event - > mmap . pid ) ;
struct map * map = map__new ( & event - > mmap ) ;
2009-06-04 18:53:49 +04:00
dprintf ( " %p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s \n " ,
2009-06-04 01:14:49 +04:00
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
2009-06-04 18:53:49 +04:00
event - > mmap . pid ,
2009-06-04 01:14:49 +04:00
( void * ) ( long ) event - > mmap . start ,
( void * ) ( long ) event - > mmap . len ,
( void * ) ( long ) event - > mmap . pgoff ,
event - > mmap . filename ) ;
if ( thread = = NULL | | map = = NULL ) {
dprintf ( " problem processing PERF_EVENT_MMAP, skipping event. \n " ) ;
2009-06-04 15:41:22 +04:00
return 0 ;
2009-06-04 01:14:49 +04:00
}
thread__insert_map ( thread , map ) ;
total_mmap + + ;
return 0 ;
}
static int
process_comm_event ( event_t * event , unsigned long offset , unsigned long head )
{
struct thread * thread = threads__findnew ( event - > comm . pid ) ;
dprintf ( " %p [%p]: PERF_EVENT_COMM: %s:%d \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
event - > comm . comm , event - > comm . pid ) ;
if ( thread = = NULL | |
thread__set_comm ( thread , event - > comm . comm ) ) {
dprintf ( " problem processing PERF_EVENT_COMM, skipping event. \n " ) ;
return - 1 ;
2009-05-18 19:45:42 +04:00
}
2009-06-04 01:14:49 +04:00
total_comm + + ;
return 0 ;
}
2009-06-04 18:53:49 +04:00
static int
process_fork_event ( event_t * event , unsigned long offset , unsigned long head )
{
struct thread * thread = threads__findnew ( event - > fork . pid ) ;
struct thread * parent = threads__findnew ( event - > fork . ppid ) ;
dprintf ( " %p [%p]: PERF_EVENT_FORK: %d:%d \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
event - > fork . pid , event - > fork . ppid ) ;
if ( ! thread | | ! parent | | thread__fork ( thread , parent ) ) {
dprintf ( " problem processing PERF_EVENT_FORK, skipping event. \n " ) ;
return - 1 ;
}
total_fork + + ;
return 0 ;
}
2009-06-05 20:07:51 +04:00
static int
process_period_event ( event_t * event , unsigned long offset , unsigned long head )
{
dprintf ( " %p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
event - > period . time ,
event - > period . id ,
event - > period . sample_period ) ;
return 0 ;
}
2009-06-04 01:14:49 +04:00
static int
process_event ( event_t * event , unsigned long offset , unsigned long head )
{
if ( event - > header . misc & PERF_EVENT_MISC_OVERFLOW )
return process_overflow_event ( event , offset , head ) ;
switch ( event - > header . type ) {
case PERF_EVENT_MMAP :
return process_mmap_event ( event , offset , head ) ;
case PERF_EVENT_COMM :
return process_comm_event ( event , offset , head ) ;
2009-06-04 18:53:49 +04:00
case PERF_EVENT_FORK :
return process_fork_event ( event , offset , head ) ;
2009-06-05 20:07:51 +04:00
case PERF_EVENT_PERIOD :
return process_period_event ( event , offset , head ) ;
2009-06-04 01:29:14 +04:00
/*
* We dont process them right now but they are fine :
*/
2009-06-04 18:53:49 +04:00
2009-06-04 01:29:14 +04:00
case PERF_EVENT_THROTTLE :
case PERF_EVENT_UNTHROTTLE :
return 0 ;
2009-06-04 01:14:49 +04:00
default :
return - 1 ;
}
return 0 ;
}
static int __cmd_report ( void )
{
2009-06-04 01:14:49 +04:00
int ret , rc = EXIT_FAILURE ;
2009-06-04 01:14:49 +04:00
unsigned long offset = 0 ;
unsigned long head = 0 ;
struct stat stat ;
event_t * event ;
uint32_t size ;
2009-06-04 01:14:49 +04:00
char * buf ;
2009-06-04 01:14:49 +04:00
register_idle_thread ( ) ;
input = open ( input_name , O_RDONLY ) ;
if ( input < 0 ) {
perror ( " failed to open file " ) ;
exit ( - 1 ) ;
}
ret = fstat ( input , & stat ) ;
if ( ret < 0 ) {
perror ( " failed to stat file " ) ;
exit ( - 1 ) ;
}
if ( ! stat . st_size ) {
fprintf ( stderr , " zero-sized file, nothing to do! \n " ) ;
exit ( 0 ) ;
}
if ( load_kernel ( ) < 0 ) {
perror ( " failed to load kernel symbols " ) ;
return EXIT_FAILURE ;
}
if ( ! full_paths ) {
if ( getcwd ( __cwd , sizeof ( __cwd ) ) = = NULL ) {
perror ( " failed to get the current directory " ) ;
return EXIT_FAILURE ;
}
cwdlen = strlen ( cwd ) ;
} else {
cwd = NULL ;
cwdlen = 0 ;
}
remap :
buf = ( char * ) mmap ( NULL , page_size * mmap_window , PROT_READ ,
MAP_SHARED , input , offset ) ;
if ( buf = = MAP_FAILED ) {
perror ( " failed to mmap file " ) ;
exit ( - 1 ) ;
}
more :
event = ( event_t * ) ( buf + head ) ;
size = event - > header . size ;
if ( ! size )
size = 8 ;
if ( head + event - > header . size > = page_size * mmap_window ) {
unsigned long shift = page_size * ( head / page_size ) ;
int ret ;
ret = munmap ( buf , page_size * mmap_window ) ;
assert ( ret = = 0 ) ;
offset + = shift ;
head - = shift ;
goto remap ;
}
size = event - > header . size ;
2009-06-05 20:07:51 +04:00
dprintf ( " %p [%p]: event: %d \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) event - > header . size ,
event - > header . type ) ;
2009-06-04 01:14:49 +04:00
if ( ! size | | process_event ( event , offset , head ) < 0 ) {
2009-06-03 11:38:58 +04:00
dprintf ( " %p [%p]: skipping unknown header type: %d \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
event - > header . type ) ;
2009-05-27 15:35:35 +04:00
2009-05-26 20:53:17 +04:00
total_unknown + + ;
2009-05-26 22:51:47 +04:00
/*
* assume we lost track of the stream , check alignment , and
* increment a single u64 in the hope to catch on again ' soon ' .
*/
if ( unlikely ( head & 7 ) )
head & = ~ 7ULL ;
size = 8 ;
2009-05-26 20:48:58 +04:00
}
2009-05-18 19:45:42 +04:00
2009-05-26 22:51:47 +04:00
head + = size ;
2009-05-26 21:03:36 +04:00
2009-05-18 19:45:42 +04:00
if ( offset + head < stat . st_size )
goto more ;
rc = EXIT_SUCCESS ;
close ( input ) ;
2009-05-26 20:48:58 +04:00
2009-06-03 11:38:58 +04:00
dprintf ( " IP events: %10ld \n " , total ) ;
dprintf ( " mmap events: %10ld \n " , total_mmap ) ;
dprintf ( " comm events: %10ld \n " , total_comm ) ;
2009-06-04 18:53:49 +04:00
dprintf ( " fork events: %10ld \n " , total_fork ) ;
2009-06-03 11:38:58 +04:00
dprintf ( " unknown events: %10ld \n " , total_unknown ) ;
2009-05-26 20:48:58 +04:00
2009-06-03 11:38:58 +04:00
if ( dump_trace )
2009-05-26 20:48:58 +04:00
return 0 ;
2009-06-04 20:54:00 +04:00
if ( verbose > = 3 )
threads__fprintf ( stdout ) ;
2009-05-27 22:20:24 +04:00
if ( verbose > = 2 )
2009-05-27 11:10:38 +04:00
dsos__fprintf ( stdout ) ;
2009-06-03 14:37:36 +04:00
collapse__resort ( ) ;
2009-05-27 22:20:24 +04:00
output__resort ( ) ;
output__fprintf ( stdout , total ) ;
2009-05-18 19:45:42 +04:00
return rc ;
}
2009-05-26 11:17:18 +04:00
static const char * const report_usage [ ] = {
" perf report [<options>] <command> " ,
NULL
} ;
static const struct option options [ ] = {
OPT_STRING ( ' i ' , " input " , & input_name , " file " ,
" input file name " ) ,
2009-05-27 02:46:14 +04:00
OPT_BOOLEAN ( ' v ' , " verbose " , & verbose ,
" be more verbose (show symbol address, etc) " ) ,
2009-05-26 20:48:58 +04:00
OPT_BOOLEAN ( ' D ' , " dump-raw-trace " , & dump_trace ,
" dump raw trace in ASCII " ) ,
2009-05-27 22:20:23 +04:00
OPT_STRING ( ' k ' , " vmlinux " , & vmlinux , " file " , " vmlinux pathname " ) ,
2009-05-28 12:52:00 +04:00
OPT_STRING ( ' s ' , " sort " , & sort_order , " key[,key2...] " ,
" sort by key(s): pid, comm, dso, symbol. Default: pid,symbol " ) ,
2009-05-29 20:48:59 +04:00
OPT_BOOLEAN ( ' P ' , " full-paths " , & full_paths ,
" Don't shorten the pathnames taking into account the cwd " ) ,
2009-05-26 11:17:18 +04:00
OPT_END ( )
} ;
2009-06-03 12:07:39 +04:00
static void setup_sorting ( void )
{
char * tmp , * tok , * str = strdup ( sort_order ) ;
for ( tok = strtok_r ( str , " , " , & tmp ) ;
tok ; tok = strtok_r ( NULL , " , " , & tmp ) ) {
if ( sort_dimension__add ( tok ) < 0 ) {
error ( " Unknown --sort key: `%s' " , tok ) ;
usage_with_options ( report_usage , options ) ;
}
}
free ( str ) ;
}
2009-05-26 11:17:18 +04:00
int cmd_report ( int argc , const char * * argv , const char * prefix )
{
2009-05-28 21:55:04 +04:00
symbol__init ( ) ;
2009-05-26 11:17:18 +04:00
page_size = getpagesize ( ) ;
2009-06-04 18:24:37 +04:00
argc = parse_options ( argc , argv , options , report_usage , 0 ) ;
2009-05-26 11:17:18 +04:00
2009-05-27 22:20:25 +04:00
setup_sorting ( ) ;
2009-06-04 18:24:37 +04:00
/*
* Any ( unrecognized ) arguments left ?
*/
if ( argc )
usage_with_options ( report_usage , options ) ;
2009-05-27 11:50:13 +04:00
setup_pager ( ) ;
2009-05-26 11:17:18 +04:00
return __cmd_report ( ) ;
}