2009-05-26 09:17:18 +02:00
# include "util/util.h"
2009-05-27 09:10:38 +02:00
# include "builtin.h"
2009-05-26 09:17:18 +02:00
2009-05-18 16:24:49 -03:00
# include "util/list.h"
2009-05-27 09:50:13 +02:00
# include "util/cache.h"
2009-05-18 16:24:49 -03:00
# include "util/rbtree.h"
2009-05-28 14:55:04 -03:00
# include "util/symbol.h"
2009-05-18 12:45:42 -03:00
2009-05-26 09:17:18 +02:00
# include "perf.h"
# include "util/parse-options.h"
# include "util/parse-events.h"
2009-05-18 12:45:42 -03:00
# define SHOW_KERNEL 1
# define SHOW_USER 2
# define SHOW_HV 4
2009-05-27 09:33:18 +02:00
static char const * input_name = " perf.data " ;
2009-05-27 20:20:23 +02:00
static char * vmlinux = NULL ;
2009-05-27 20:20:26 +02:00
static char * sort_order = " pid,symbol " ;
2009-05-18 12:45:42 -03:00
static int input ;
static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV ;
2009-05-26 18:48:58 +02:00
static int dump_trace = 0 ;
2009-05-27 09:10:38 +02:00
static int verbose ;
2009-05-26 18:48:58 +02:00
2009-05-18 12:45:42 -03:00
static unsigned long page_size ;
static unsigned long mmap_window = 32 ;
2009-05-26 09:17:18 +02:00
const char * perf_event_names [ ] = {
2009-05-18 12:45:42 -03:00
[ PERF_EVENT_MMAP ] = " PERF_EVENT_MMAP " ,
[ PERF_EVENT_MUNMAP ] = " PERF_EVENT_MUNMAP " ,
[ PERF_EVENT_COMM ] = " PERF_EVENT_COMM " ,
} ;
struct ip_event {
struct perf_event_header header ;
__u64 ip ;
__u32 pid , tid ;
} ;
struct mmap_event {
struct perf_event_header header ;
__u32 pid , tid ;
__u64 start ;
__u64 len ;
__u64 pgoff ;
char filename [ PATH_MAX ] ;
} ;
struct comm_event {
struct perf_event_header header ;
__u32 pid , tid ;
char comm [ 16 ] ;
} ;
typedef union event_union {
struct perf_event_header header ;
struct ip_event ip ;
struct mmap_event mmap ;
struct comm_event comm ;
} event_t ;
static LIST_HEAD ( dsos ) ;
static struct dso * kernel_dso ;
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 13:35:35 +02:00
int nr ;
2009-05-18 12:45:42 -03:00
if ( dso = = NULL ) {
2009-05-28 14:55:13 -03:00
dso = dso__new ( name , 0 ) ;
2009-05-27 13:35:35 +02:00
if ( ! dso )
goto out_delete_dso ;
nr = dso__load ( dso ) ;
if ( nr < 0 ) {
fprintf ( stderr , " Failed to open: %s \n " , name ) ;
2009-05-18 12:45:42 -03:00
goto out_delete_dso ;
2009-05-27 13:35:35 +02:00
}
if ( ! nr ) {
fprintf ( stderr ,
" Failed to find debug symbols for: %s, maybe install a debug package? \n " ,
name ) ;
}
2009-05-18 12:45:42 -03:00
dsos__add ( dso ) ;
}
return dso ;
out_delete_dso :
dso__delete ( dso ) ;
return NULL ;
}
2009-05-27 09:10:38 +02:00
static void dsos__fprintf ( FILE * fp )
2009-05-18 12:45:42 -03:00
{
struct dso * pos ;
list_for_each_entry ( pos , & dsos , node )
dso__fprintf ( pos , fp ) ;
}
2009-05-27 20:20:23 +02:00
static int load_kernel ( void )
{
2009-05-28 14:55:04 -03:00
int err = - 1 ;
2009-05-27 20:20:23 +02:00
2009-05-28 14:55:13 -03:00
kernel_dso = dso__new ( " [kernel] " , 0 ) ;
2009-05-27 20:20:23 +02:00
if ( ! kernel_dso )
2009-05-28 14:55:04 -03:00
return - 1 ;
2009-05-27 20:20:23 +02:00
2009-05-28 14:55:04 -03:00
if ( vmlinux )
err = dso__load_vmlinux ( kernel_dso , vmlinux ) ;
2009-05-27 20:20:23 +02:00
2009-05-28 14:55:04 -03:00
if ( err )
err = dso__load_kallsyms ( kernel_dso ) ;
2009-05-27 20:20:23 +02:00
2009-05-28 14:55:04 -03:00
if ( err ) {
dso__delete ( kernel_dso ) ;
kernel_dso = NULL ;
} else
dsos__add ( kernel_dso ) ;
2009-05-27 20:20:23 +02:00
2009-05-28 14:55:04 -03:00
return err ;
2009-05-27 20:20:23 +02:00
}
2009-05-18 12:45:42 -03:00
struct map {
struct list_head node ;
uint64_t start ;
uint64_t end ;
uint64_t pgoff ;
struct dso * dso ;
} ;
static struct map * map__new ( struct mmap_event * event )
{
struct map * self = malloc ( sizeof ( * self ) ) ;
if ( self ! = NULL ) {
self - > start = event - > start ;
self - > end = event - > start + event - > len ;
self - > pgoff = event - > pgoff ;
self - > dso = dsos__findnew ( event - > filename ) ;
if ( self - > dso = = NULL )
goto out_delete ;
}
return self ;
out_delete :
free ( self ) ;
return NULL ;
}
2009-05-26 16:19:04 -03:00
struct thread ;
2009-05-18 12:45:42 -03:00
struct thread {
2009-05-19 09:30:23 -03:00
struct rb_node rb_node ;
2009-05-18 12:45:42 -03: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 ;
self - > comm = NULL ;
INIT_LIST_HEAD ( & self - > maps ) ;
}
return self ;
}
static int thread__set_comm ( struct thread * self , const char * comm )
{
self - > comm = strdup ( comm ) ;
return self - > comm ? 0 : - ENOMEM ;
}
2009-05-27 09:10:38 +02:00
static struct rb_root threads ;
2009-05-18 12:45:42 -03:00
2009-05-19 09:30:23 -03:00
static struct thread * threads__findnew ( pid_t pid )
2009-05-18 12:45:42 -03:00
{
2009-05-19 09:30:23 -03:00
struct rb_node * * p = & threads . rb_node ;
struct rb_node * parent = NULL ;
struct thread * th ;
2009-05-18 12:45:42 -03:00
2009-05-19 09:30:23 -03:00
while ( * p ! = NULL ) {
parent = * p ;
th = rb_entry ( parent , struct thread , rb_node ) ;
2009-05-18 12:45:42 -03:00
2009-05-19 09:30:23 -03:00
if ( th - > pid = = pid )
return th ;
2009-05-18 12:45:42 -03:00
2009-05-19 09:30:23 -03:00
if ( pid < th - > pid )
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
2009-05-18 12:45:42 -03:00
}
2009-05-19 09:30:23 -03:00
th = thread__new ( pid ) ;
if ( th ! = NULL ) {
rb_link_node ( & th - > rb_node , parent , p ) ;
rb_insert_color ( & th - > rb_node , & threads ) ;
}
return th ;
2009-05-18 12:45:42 -03:00
}
static void thread__insert_map ( struct thread * self , struct map * map )
{
list_add_tail ( & map - > node , & self - > maps ) ;
}
static struct map * thread__find_map ( struct thread * self , uint64_t ip )
{
2009-05-27 09:10:38 +02:00
struct map * pos ;
2009-05-18 12:45:42 -03: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-05-27 20:20:24 +02: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 20:20:25 +02:00
/*
* configurable sorting bits
*/
struct sort_entry {
struct list_head list ;
2009-05-28 11:08:33 +02:00
char * header ;
2009-05-27 20:20:25 +02:00
int64_t ( * cmp ) ( struct hist_entry * , struct hist_entry * ) ;
size_t ( * print ) ( FILE * fp , struct hist_entry * ) ;
} ;
2009-05-27 20:20:24 +02:00
static int64_t
2009-05-27 20:20:25 +02:00
sort__thread_cmp ( struct hist_entry * left , struct hist_entry * right )
2009-05-27 20:20:24 +02:00
{
2009-05-27 20:20:25 +02:00
return right - > thread - > pid - left - > thread - > pid ;
}
static size_t
sort__thread_print ( FILE * fp , struct hist_entry * self )
{
2009-05-28 11:08:33 +02:00
return fprintf ( fp , " %16s:%5d " , self - > thread - > comm ? : " " , self - > thread - > pid ) ;
2009-05-27 20:20:25 +02:00
}
2009-05-27 20:20:24 +02:00
2009-05-27 20:20:25 +02:00
static struct sort_entry sort_thread = {
2009-05-28 11:08:33 +02:00
. header = " Command: Pid " ,
2009-05-27 20:20:25 +02:00
. cmp = sort__thread_cmp ,
. print = sort__thread_print ,
} ;
2009-05-27 20:20:27 +02:00
static int64_t
sort__comm_cmp ( struct hist_entry * left , struct hist_entry * right )
{
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-05-27 21:36:22 +02:00
return fprintf ( fp , " %16s " , self - > thread - > comm ? : " <unknown> " ) ;
2009-05-27 20:20:27 +02:00
}
static struct sort_entry sort_comm = {
2009-05-28 11:08:33 +02:00
. header = " Command " ,
2009-05-27 20:20:27 +02:00
. cmp = sort__comm_cmp ,
. print = sort__comm_print ,
} ;
2009-05-27 20:20:28 +02: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-05-27 21:36:22 +02:00
return fprintf ( fp , " %64s " , self - > dso ? self - > dso - > name : " <unknown> " ) ;
2009-05-27 20:20:28 +02:00
}
static struct sort_entry sort_dso = {
2009-05-28 11:08:33 +02:00
. header = " Shared Object " ,
2009-05-27 20:20:28 +02:00
. cmp = sort__dso_cmp ,
. print = sort__dso_print ,
} ;
2009-05-27 20:20:25 +02:00
static int64_t
sort__sym_cmp ( struct hist_entry * left , struct hist_entry * right )
{
uint64_t ip_l , ip_r ;
2009-05-27 20:20:24 +02: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 20:20:25 +02:00
static size_t
sort__sym_print ( FILE * fp , struct hist_entry * self )
{
size_t ret = 0 ;
if ( verbose )
2009-05-27 21:36:22 +02:00
ret + = fprintf ( fp , " %#018llx " , ( unsigned long long ) self - > ip ) ;
2009-05-27 20:20:25 +02:00
2009-05-28 11:08:33 +02:00
ret + = fprintf ( fp , " %s: %s " ,
self - > dso ? self - > dso - > name : " <unknown> " ,
self - > sym ? self - > sym - > name : " <unknown> " ) ;
2009-05-27 20:20:25 +02:00
return ret ;
}
static struct sort_entry sort_sym = {
2009-05-28 11:08:33 +02:00
. header = " Shared Object: Symbol " ,
. cmp = sort__sym_cmp ,
. print = sort__sym_print ,
2009-05-27 20:20:25 +02:00
} ;
2009-05-27 20:20:26 +02:00
struct sort_dimension {
char * name ;
struct sort_entry * entry ;
int taken ;
} ;
static struct sort_dimension sort_dimensions [ ] = {
{ . name = " pid " , . entry = & sort_thread , } ,
2009-05-27 20:20:27 +02:00
{ . name = " comm " , . entry = & sort_comm , } ,
2009-05-27 20:20:28 +02:00
{ . name = " dso " , . entry = & sort_dso , } ,
2009-05-27 20:20:26 +02:00
{ . name = " symbol " , . entry = & sort_sym , } ,
} ;
2009-05-27 20:20:25 +02:00
static LIST_HEAD ( hist_entry__sort_list ) ;
2009-05-27 20:20:26 +02: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 ;
if ( strcmp ( tok , sd - > name ) )
continue ;
list_add_tail ( & sd - > entry - > list , & hist_entry__sort_list ) ;
sd - > taken = 1 ;
return 0 ;
}
return - ESRCH ;
}
2009-05-27 20:20:25 +02:00
static void setup_sorting ( void )
{
2009-05-27 20:20:26 +02:00
char * tmp , * tok , * str = strdup ( sort_order ) ;
for ( tok = strtok_r ( str , " , " , & tmp ) ;
tok ; tok = strtok_r ( NULL , " , " , & tmp ) )
sort_dimension__add ( tok ) ;
free ( str ) ;
2009-05-27 20:20:25 +02: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 ;
}
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-05-27 21:36:22 +02:00
ret = fprintf ( fp , " %5.2f%% " ,
2009-05-27 20:20:25 +02:00
( self - > count * 100.0 ) / total_samples ) ;
} else
ret = fprintf ( fp , " %12d " , self - > count ) ;
list_for_each_entry ( se , & hist_entry__sort_list , list )
ret + = se - > print ( fp , self ) ;
ret + = fprintf ( fp , " \n " ) ;
return ret ;
}
/*
* collect histogram counts
*/
2009-05-27 20:20:24 +02: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 12:45:42 -03:00
{
2009-05-27 20:20:24 +02: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 09:30:23 -03:00
}
2009-05-27 20:20:24 +02: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 12:45:42 -03:00
}
2009-05-27 20:20:24 +02: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 16:19:04 -03:00
{
2009-05-27 20:20:24 +02:00
struct rb_node * * p = & output_hists . rb_node ;
2009-05-26 16:19:04 -03:00
struct rb_node * parent = NULL ;
2009-05-27 20:20:24 +02:00
struct hist_entry * iter ;
2009-05-26 16:19:04 -03:00
while ( * p ! = NULL ) {
parent = * p ;
2009-05-27 20:20:24 +02:00
iter = rb_entry ( parent , struct hist_entry , rb_node ) ;
2009-05-26 16:19:04 -03:00
2009-05-27 20:20:24 +02:00
if ( he - > count > iter - > count )
2009-05-26 16:19:04 -03:00
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
}
2009-05-27 20:20:24 +02:00
rb_link_node ( & he - > rb_node , parent , p ) ;
rb_insert_color ( & he - > rb_node , & output_hists ) ;
2009-05-26 16:19:04 -03:00
}
2009-05-27 20:20:24 +02:00
static void output__resort ( void )
2009-05-26 16:19:04 -03:00
{
2009-05-27 20:20:24 +02:00
struct rb_node * next = rb_first ( & hist ) ;
struct hist_entry * n ;
2009-05-26 16:19:04 -03:00
2009-05-27 20:20:24 +02:00
while ( next ) {
n = rb_entry ( next , struct hist_entry , rb_node ) ;
next = rb_next ( & n - > rb_node ) ;
2009-05-26 16:19:04 -03:00
2009-05-27 20:20:24 +02:00
rb_erase ( & n - > rb_node , & hist ) ;
output__insert_entry ( n ) ;
2009-05-26 16:19:04 -03:00
}
}
2009-05-27 20:20:24 +02:00
static size_t output__fprintf ( FILE * fp , uint64_t total_samples )
2009-05-26 16:19:04 -03:00
{
2009-05-27 20:20:24 +02:00
struct hist_entry * pos ;
2009-05-27 21:36:22 +02:00
struct sort_entry * se ;
2009-05-26 16:19:04 -03:00
struct rb_node * nd ;
size_t ret = 0 ;
2009-05-28 11:08:33 +02:00
fprintf ( fp , " # \n " ) ;
fprintf ( fp , " # Overhead " ) ;
list_for_each_entry ( se , & hist_entry__sort_list , list )
fprintf ( fp , " %s " , se - > header ) ;
fprintf ( fp , " \n " ) ;
fprintf ( fp , " # ........ " ) ;
2009-05-27 21:36:22 +02:00
list_for_each_entry ( se , & hist_entry__sort_list , list ) {
2009-05-28 11:08:33 +02:00
int i ;
fprintf ( fp , " " ) ;
for ( i = 0 ; i < strlen ( se - > header ) ; i + + )
fprintf ( fp , " . " ) ;
2009-05-27 21:36:22 +02:00
}
2009-05-28 11:08:33 +02:00
fprintf ( fp , " \n " ) ;
fprintf ( fp , " # \n " ) ;
2009-05-27 21:36:22 +02:00
2009-05-27 20:20:24 +02: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 16:19:04 -03:00
}
return ret ;
}
2009-05-27 20:20:24 +02:00
2009-05-26 09:17:18 +02:00
static int __cmd_report ( void )
2009-05-18 12:45:42 -03:00
{
unsigned long offset = 0 ;
unsigned long head = 0 ;
struct stat stat ;
char * buf ;
event_t * event ;
int ret , rc = EXIT_FAILURE ;
2009-05-26 20:51:47 +02:00
uint32_t size ;
2009-05-26 19:03:36 +02:00
unsigned long total = 0 , total_mmap = 0 , total_comm = 0 , total_unknown = 0 ;
2009-05-18 12:45:42 -03:00
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 ) ;
}
2009-05-27 20:20:23 +02:00
if ( load_kernel ( ) < 0 ) {
2009-05-28 14:55:04 -03:00
perror ( " failed to load kernel symbols " ) ;
2009-05-18 12:45:42 -03:00
return EXIT_FAILURE ;
}
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 ) ;
2009-05-26 20:51:47 +02:00
size = event - > header . size ;
if ( ! size )
size = 8 ;
2009-05-18 12:45:42 -03:00
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 ;
}
2009-05-26 20:51:47 +02:00
size = event - > header . size ;
if ( ! size )
goto broken_event ;
2009-05-18 12:45:42 -03:00
if ( event - > header . misc & PERF_EVENT_MISC_OVERFLOW ) {
char level ;
int show = 0 ;
struct dso * dso = NULL ;
struct thread * thread = threads__findnew ( event - > ip . pid ) ;
2009-05-26 15:30:22 +02:00
uint64_t ip = event - > ip . ip ;
2009-05-27 20:20:24 +02:00
struct map * map = NULL ;
2009-05-18 12:45:42 -03:00
2009-05-26 18:48:58 +02:00
if ( dump_trace ) {
2009-05-26 19:03:36 +02:00
fprintf ( stderr , " %p [%p]: PERF_EVENT (IP, %d): %d: %p \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
2009-05-26 18:48:58 +02:00
event - > header . misc ,
event - > ip . pid ,
2009-05-27 09:10:38 +02:00
( void * ) ( long ) ip ) ;
2009-05-26 18:48:58 +02:00
}
2009-05-19 09:30:23 -03:00
if ( thread = = NULL ) {
2009-05-27 22:13:17 +02:00
fprintf ( stderr , " problem processing %d event, skipping it. \n " ,
2009-05-19 09:30:23 -03:00
event - > header . type ) ;
2009-05-27 22:13:17 +02:00
goto broken_event ;
2009-05-19 09:30:23 -03:00
}
2009-05-18 12:45:42 -03:00
if ( event - > header . misc & PERF_EVENT_MISC_KERNEL ) {
show = SHOW_KERNEL ;
level = ' k ' ;
2009-05-27 20:20:24 +02:00
2009-05-18 12:45:42 -03:00
dso = kernel_dso ;
2009-05-27 20:20:24 +02:00
2009-05-18 12:45:42 -03:00
} else if ( event - > header . misc & PERF_EVENT_MISC_USER ) {
2009-05-27 09:10:38 +02:00
2009-05-18 12:45:42 -03:00
show = SHOW_USER ;
level = ' . ' ;
2009-05-27 09:10:38 +02:00
map = thread__find_map ( thread , ip ) ;
2009-05-26 15:30:22 +02:00
if ( map ! = NULL ) {
2009-05-18 12:45:42 -03:00
dso = map - > dso ;
2009-05-26 15:30:22 +02:00
ip - = map - > start + map - > pgoff ;
}
2009-05-27 20:20:24 +02:00
2009-05-18 12:45:42 -03:00
} else {
show = SHOW_HV ;
level = ' H ' ;
}
if ( show & show_mask ) {
2009-05-26 15:30:22 +02:00
struct symbol * sym = dso__find_symbol ( dso , ip ) ;
2009-05-18 12:45:42 -03:00
2009-05-27 20:20:24 +02:00
if ( hist_entry__add ( thread , map , dso , sym , ip , level ) ) {
fprintf ( stderr ,
2009-05-27 22:13:17 +02:00
" problem incrementing symbol count, skipping event \n " ) ;
goto broken_event ;
2009-05-19 09:30:23 -03:00
}
2009-05-18 12:45:42 -03:00
}
total + + ;
} else switch ( event - > header . type ) {
case PERF_EVENT_MMAP : {
struct thread * thread = threads__findnew ( event - > mmap . pid ) ;
struct map * map = map__new ( & event - > mmap ) ;
2009-05-26 18:48:58 +02:00
if ( dump_trace ) {
2009-05-26 19:03:36 +02:00
fprintf ( stderr , " %p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
2009-05-27 09:10:38 +02:00
( void * ) ( long ) event - > mmap . start ,
( void * ) ( long ) event - > mmap . len ,
( void * ) ( long ) event - > mmap . pgoff ,
2009-05-26 18:48:58 +02:00
event - > mmap . filename ) ;
}
2009-05-19 09:30:23 -03:00
if ( thread = = NULL | | map = = NULL ) {
2009-05-27 22:13:17 +02:00
fprintf ( stderr , " problem processing PERF_EVENT_MMAP, skipping event. \n " ) ;
goto broken_event ;
2009-05-19 09:30:23 -03:00
}
2009-05-18 12:45:42 -03:00
thread__insert_map ( thread , map ) ;
2009-05-26 18:48:58 +02:00
total_mmap + + ;
2009-05-18 12:45:42 -03:00
break ;
}
case PERF_EVENT_COMM : {
struct thread * thread = threads__findnew ( event - > comm . pid ) ;
2009-05-26 18:48:58 +02:00
if ( dump_trace ) {
2009-05-26 19:03:36 +02:00
fprintf ( stderr , " %p [%p]: PERF_EVENT_COMM: %s:%d \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
2009-05-26 18:48:58 +02:00
event - > comm . comm , event - > comm . pid ) ;
}
2009-05-18 12:45:42 -03:00
if ( thread = = NULL | |
2009-05-19 09:30:23 -03:00
thread__set_comm ( thread , event - > comm . comm ) ) {
2009-05-27 22:13:17 +02:00
fprintf ( stderr , " problem processing PERF_EVENT_COMM, skipping event. \n " ) ;
goto broken_event ;
2009-05-19 09:30:23 -03:00
}
2009-05-26 18:48:58 +02:00
total_comm + + ;
2009-05-18 12:45:42 -03:00
break ;
}
2009-05-26 18:48:58 +02:00
default : {
2009-05-26 20:51:47 +02:00
broken_event :
2009-05-27 13:35:35 +02:00
if ( dump_trace )
fprintf ( stderr , " %p [%p]: skipping unknown header type: %d \n " ,
( void * ) ( offset + head ) ,
( void * ) ( long ) ( event - > header . size ) ,
event - > header . type ) ;
2009-05-26 18:53:17 +02:00
total_unknown + + ;
2009-05-26 20:51:47 +02: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 18:48:58 +02:00
}
2009-05-18 12:45:42 -03:00
}
2009-05-26 20:51:47 +02:00
head + = size ;
2009-05-26 19:03:36 +02:00
2009-05-18 12:45:42 -03:00
if ( offset + head < stat . st_size )
goto more ;
rc = EXIT_SUCCESS ;
close ( input ) ;
2009-05-26 18:48:58 +02:00
if ( dump_trace ) {
2009-05-26 18:53:17 +02:00
fprintf ( stderr , " IP events: %10ld \n " , total ) ;
fprintf ( stderr , " mmap events: %10ld \n " , total_mmap ) ;
fprintf ( stderr , " comm events: %10ld \n " , total_comm ) ;
fprintf ( stderr , " unknown events: %10ld \n " , total_unknown ) ;
2009-05-26 18:48:58 +02:00
return 0 ;
}
2009-05-27 20:20:24 +02:00
if ( verbose > = 2 )
2009-05-27 09:10:38 +02:00
dsos__fprintf ( stdout ) ;
2009-05-27 20:20:24 +02:00
output__resort ( ) ;
output__fprintf ( stdout , total ) ;
2009-05-18 12:45:42 -03:00
return rc ;
}
2009-05-26 09:17:18 +02: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-26 19:46:14 -03:00
OPT_BOOLEAN ( ' v ' , " verbose " , & verbose ,
" be more verbose (show symbol address, etc) " ) ,
2009-05-26 18:48:58 +02:00
OPT_BOOLEAN ( ' D ' , " dump-raw-trace " , & dump_trace ,
" dump raw trace in ASCII " ) ,
2009-05-27 20:20:23 +02:00
OPT_STRING ( ' k ' , " vmlinux " , & vmlinux , " file " , " vmlinux pathname " ) ,
2009-05-28 10:52:00 +02:00
OPT_STRING ( ' s ' , " sort " , & sort_order , " key[,key2...] " ,
" sort by key(s): pid, comm, dso, symbol. Default: pid,symbol " ) ,
2009-05-26 09:17:18 +02:00
OPT_END ( )
} ;
int cmd_report ( int argc , const char * * argv , const char * prefix )
{
2009-05-28 14:55:04 -03:00
symbol__init ( ) ;
2009-05-26 09:17:18 +02:00
page_size = getpagesize ( ) ;
parse_options ( argc , argv , options , report_usage , 0 ) ;
2009-05-27 20:20:25 +02:00
setup_sorting ( ) ;
2009-05-27 09:50:13 +02:00
setup_pager ( ) ;
2009-05-26 09:17:18 +02:00
return __cmd_report ( ) ;
}