2009-12-15 01:09:31 +03:00
/*
* builtin - diff . c
*
* Builtin diff command : Analyze two perf . data input files , look up and read
* DSOs and symbol information , sort them and produce a diff .
*/
# include "builtin.h"
# include "util/debug.h"
# include "util/event.h"
# include "util/hist.h"
2011-11-28 13:56:39 +04:00
# include "util/evsel.h"
2012-09-06 19:46:55 +04:00
# include "util/evlist.h"
2009-12-15 01:09:31 +03:00
# include "util/session.h"
2011-11-28 14:30:20 +04:00
# include "util/tool.h"
2009-12-15 01:09:31 +03:00
# include "util/sort.h"
# include "util/symbol.h"
# include "util/util.h"
# include <stdlib.h>
perf diff: Use perf_session__fprintf_hists just like 'perf record'
That means that almost everything you can do with 'perf report'
can be done with 'perf diff', for instance:
$ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2699
samples) ] $ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2687
samples) ] perf diff | head -8
9.02% +1.00% find libc-2.10.1.so [.] _IO_vfprintf_internal
2.91% -1.00% find [kernel] [k] __kmalloc
2.85% -1.00% find [kernel] [k] ext4_htree_store_dirent
1.99% -1.00% find [kernel] [k] _atomic_dec_and_lock
2.44% find [kernel] [k] half_md4_transform
$
So if you want to zoom into libc:
$ perf diff --dsos libc-2.10.1.so | head -8
37.34% find [.] _IO_vfprintf_internal
10.34% find [.] __GI_memmove
8.25% +2.00% find [.] _int_malloc
5.07% -1.00% find [.] __GI_mempcpy
7.62% +2.00% find [.] _int_free
$
And if there were multiple commands using libc, it is also
possible to aggregate them all by using --sort symbol:
$ perf diff --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% [.] __GI_mempcpy
7.62% +2.00% [.] _int_free
$
The displacement column now is off by default, to use it:
perf diff -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% +2 [.] __GI_mempcpy
7.62% +2.00% -1 [.] _int_free
$
Using -t/--field-separator can be used for scripting:
$ perf diff -t, -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34, , ,[.] _IO_vfprintf_internal
10.34, , ,[.] __GI_memmove
8.25,+2.00%, ,[.] _int_malloc
5.07,-1.00%, +2,[.] __GI_mempcpy
7.62,+2.00%, -1,[.] _int_free
6.99,+1.00%, -1,[.] _IO_new_file_xsputn
1.89,-2.00%, +4,[.] __readdir64
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1260978567-550-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-12-16 18:49:27 +03:00
static char const * input_old = " perf.data.old " ,
* input_new = " perf.data " ;
2009-12-16 19:09:53 +03:00
static char diff__default_sort_order [ ] = " dso,symbol " ;
2010-04-13 12:37:33 +04:00
static bool force ;
2012-10-05 18:44:44 +04:00
static bool show_period ;
2012-10-05 18:44:45 +04:00
static bool show_formula ;
2012-10-05 18:44:40 +04:00
static bool show_baseline_only ;
2012-10-05 18:44:42 +04:00
static bool sort_compute ;
2009-12-15 01:09:31 +03:00
2012-10-05 18:44:43 +04:00
static s64 compute_wdiff_w1 ;
static s64 compute_wdiff_w2 ;
2012-10-05 18:44:41 +04:00
enum {
COMPUTE_DELTA ,
COMPUTE_RATIO ,
2012-10-05 18:44:43 +04:00
COMPUTE_WEIGHTED_DIFF ,
2012-10-05 18:44:41 +04:00
COMPUTE_MAX ,
} ;
const char * compute_names [ COMPUTE_MAX ] = {
[ COMPUTE_DELTA ] = " delta " ,
[ COMPUTE_RATIO ] = " ratio " ,
2012-10-05 18:44:43 +04:00
[ COMPUTE_WEIGHTED_DIFF ] = " wdiff " ,
2012-10-05 18:44:41 +04:00
} ;
static int compute ;
2012-10-05 18:44:43 +04:00
static int setup_compute_opt_wdiff ( char * opt )
{
char * w1_str = opt ;
char * w2_str ;
int ret = - EINVAL ;
if ( ! opt )
goto out ;
w2_str = strchr ( opt , ' , ' ) ;
if ( ! w2_str )
goto out ;
* w2_str + + = 0x0 ;
if ( ! * w2_str )
goto out ;
compute_wdiff_w1 = strtol ( w1_str , NULL , 10 ) ;
compute_wdiff_w2 = strtol ( w2_str , NULL , 10 ) ;
if ( ! compute_wdiff_w1 | | ! compute_wdiff_w2 )
goto out ;
pr_debug ( " compute wdiff w1(% " PRId64 " ) w2(% " PRId64 " ) \n " ,
compute_wdiff_w1 , compute_wdiff_w2 ) ;
ret = 0 ;
out :
if ( ret )
pr_err ( " Failed: wrong weight data, use 'wdiff:w1,w2' \n " ) ;
return ret ;
}
static int setup_compute_opt ( char * opt )
{
if ( compute = = COMPUTE_WEIGHTED_DIFF )
return setup_compute_opt_wdiff ( opt ) ;
if ( opt ) {
pr_err ( " Failed: extra option specified '%s' " , opt ) ;
return - EINVAL ;
}
return 0 ;
}
2012-10-05 18:44:41 +04:00
static int setup_compute ( const struct option * opt , const char * str ,
int unset __maybe_unused )
{
int * cp = ( int * ) opt - > value ;
2012-10-05 18:44:43 +04:00
char * cstr = ( char * ) str ;
char buf [ 50 ] ;
2012-10-05 18:44:41 +04:00
unsigned i ;
2012-10-05 18:44:43 +04:00
char * option ;
2012-10-05 18:44:41 +04:00
if ( ! str ) {
* cp = COMPUTE_DELTA ;
return 0 ;
}
2012-10-05 18:44:42 +04:00
if ( * str = = ' + ' ) {
sort_compute = true ;
2012-10-05 18:44:43 +04:00
cstr = ( char * ) + + str ;
2012-10-05 18:44:42 +04:00
if ( ! * str )
return 0 ;
}
2012-10-05 18:44:43 +04:00
option = strchr ( str , ' : ' ) ;
if ( option ) {
unsigned len = option + + - str ;
/*
* The str data are not writeable , so we need
* to use another buffer .
*/
/* No option value is longer. */
if ( len > = sizeof ( buf ) )
return - EINVAL ;
strncpy ( buf , str , len ) ;
buf [ len ] = 0x0 ;
cstr = buf ;
}
2012-10-05 18:44:41 +04:00
for ( i = 0 ; i < COMPUTE_MAX ; i + + )
2012-10-05 18:44:43 +04:00
if ( ! strcmp ( cstr , compute_names [ i ] ) ) {
2012-10-05 18:44:41 +04:00
* cp = i ;
2012-10-05 18:44:43 +04:00
return setup_compute_opt ( option ) ;
2012-10-05 18:44:41 +04:00
}
pr_err ( " Failed: '%s' is not computation method "
2012-10-05 18:44:43 +04:00
" (use 'delta','ratio' or 'wdiff') \n " , str ) ;
2012-10-05 18:44:41 +04:00
return - EINVAL ;
}
2012-11-28 17:52:40 +04:00
double perf_diff__period_percent ( struct hist_entry * he , u64 period )
2012-10-05 18:44:42 +04:00
{
u64 total = he - > hists - > stats . total_period ;
return ( period * 100.0 ) / total ;
}
2012-11-28 17:52:40 +04:00
double perf_diff__compute_delta ( struct hist_entry * he , struct hist_entry * pair )
2012-10-05 18:44:42 +04:00
{
2012-11-28 17:52:40 +04:00
double new_percent = perf_diff__period_percent ( he , he - > stat . period ) ;
double old_percent = perf_diff__period_percent ( pair , pair - > stat . period ) ;
2012-10-05 18:44:42 +04:00
he - > diff . period_ratio_delta = new_percent - old_percent ;
he - > diff . computed = true ;
return he - > diff . period_ratio_delta ;
}
2012-11-28 17:52:40 +04:00
double perf_diff__compute_ratio ( struct hist_entry * he , struct hist_entry * pair )
2012-10-05 18:44:42 +04:00
{
double new_period = he - > stat . period ;
2012-11-28 17:52:40 +04:00
double old_period = pair - > stat . period ;
2012-10-05 18:44:42 +04:00
he - > diff . computed = true ;
2012-11-28 17:52:40 +04:00
he - > diff . period_ratio = new_period / old_period ;
2012-10-05 18:44:42 +04:00
return he - > diff . period_ratio ;
}
2012-11-28 17:52:40 +04:00
s64 perf_diff__compute_wdiff ( struct hist_entry * he , struct hist_entry * pair )
2012-10-05 18:44:43 +04:00
{
u64 new_period = he - > stat . period ;
2012-11-28 17:52:40 +04:00
u64 old_period = pair - > stat . period ;
2012-10-05 18:44:43 +04:00
he - > diff . computed = true ;
2012-11-28 17:52:40 +04:00
he - > diff . wdiff = new_period * compute_wdiff_w2 -
old_period * compute_wdiff_w1 ;
2012-10-05 18:44:43 +04:00
return he - > diff . wdiff ;
}
2012-11-28 17:52:41 +04:00
static int formula_delta ( struct hist_entry * he , struct hist_entry * pair ,
char * buf , size_t size )
2012-10-05 18:44:45 +04:00
{
return scnprintf ( buf , size ,
" (% " PRIu64 " * 100 / % " PRIu64 " ) - "
" (% " PRIu64 " * 100 / % " PRIu64 " ) " ,
he - > stat . period , he - > hists - > stats . total_period ,
pair - > stat . period , pair - > hists - > stats . total_period ) ;
}
2012-11-28 17:52:41 +04:00
static int formula_ratio ( struct hist_entry * he , struct hist_entry * pair ,
char * buf , size_t size )
2012-10-05 18:44:45 +04:00
{
double new_period = he - > stat . period ;
2012-11-28 17:52:41 +04:00
double old_period = pair - > stat . period ;
2012-10-05 18:44:45 +04:00
return scnprintf ( buf , size , " %.0F / %.0F " , new_period , old_period ) ;
}
2012-11-28 17:52:41 +04:00
static int formula_wdiff ( struct hist_entry * he , struct hist_entry * pair ,
char * buf , size_t size )
2012-10-05 18:44:45 +04:00
{
u64 new_period = he - > stat . period ;
2012-11-28 17:52:41 +04:00
u64 old_period = pair - > stat . period ;
2012-10-05 18:44:45 +04:00
return scnprintf ( buf , size ,
" (% " PRIu64 " * " " % " PRId64 " ) - (% " PRIu64 " * " " % " PRId64 " ) " ,
new_period , compute_wdiff_w2 , old_period , compute_wdiff_w1 ) ;
}
2012-11-28 17:52:41 +04:00
int perf_diff__formula ( struct hist_entry * he , struct hist_entry * pair ,
char * buf , size_t size )
2012-10-05 18:44:45 +04:00
{
switch ( compute ) {
case COMPUTE_DELTA :
2012-11-28 17:52:41 +04:00
return formula_delta ( he , pair , buf , size ) ;
2012-10-05 18:44:45 +04:00
case COMPUTE_RATIO :
2012-11-28 17:52:41 +04:00
return formula_ratio ( he , pair , buf , size ) ;
2012-10-05 18:44:45 +04:00
case COMPUTE_WEIGHTED_DIFF :
2012-11-28 17:52:41 +04:00
return formula_wdiff ( he , pair , buf , size ) ;
2012-10-05 18:44:45 +04:00
default :
BUG_ON ( 1 ) ;
}
return - 1 ;
}
perf hist: Introduce hists class and move lots of methods to it
In cbbc79a we introduced support for multiple events by introducing a
new "event_stat_id" struct and then made several perf_session methods
receive a point to it instead of a pointer to perf_session, and kept the
event_stats and hists rb_tree in perf_session.
While working on the new newt based browser, I realised that it would be
better to introduce a new class, "hists" (short for "histograms"),
renaming the "event_stat_id" struct and the perf_session methods that
were really "hists" methods, as they manipulate only struct hists
members, not touching anything in the other perf_session members.
Other optimizations, such as calculating the maximum lenght of a symbol
name present in an hists instance will be possible as we add them,
avoiding a re-traversal just for finding that information.
The rationale for the name "hists" to replace "event_stat_id" is that we
may have multiple sets of hists for the same event_stat id, as, for
instance, the 'perf diff' tool has, so event stat id is not what
characterizes what this struct and the functions that manipulate it do.
Cc: Eric B Munson <ebmunson@us.ibm.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2010-05-10 20:04:11 +04:00
static int hists__add_entry ( struct hists * self ,
2013-01-24 19:10:29 +04:00
struct addr_location * al , u64 period ,
u64 weight )
2009-12-15 01:09:31 +03:00
{
2013-01-24 19:10:29 +04:00
if ( __hists__add_entry ( self , al , NULL , period , weight ) ! = NULL )
2010-05-09 20:02:23 +04:00
return 0 ;
return - ENOMEM ;
2009-12-15 01:09:31 +03:00
}
2012-09-11 02:15:03 +04:00
static int diff__process_sample_event ( struct perf_tool * tool __maybe_unused ,
2011-11-25 14:19:45 +04:00
union perf_event * event ,
2011-01-29 18:02:00 +03:00
struct perf_sample * sample ,
2012-09-06 19:46:55 +04:00
struct perf_evsel * evsel ,
2011-11-28 13:56:39 +04:00
struct machine * machine )
2009-12-15 01:09:31 +03:00
{
struct addr_location al ;
2011-11-28 13:56:39 +04:00
if ( perf_event__preprocess_sample ( event , machine , & al , sample , NULL ) < 0 ) {
2009-12-15 01:09:31 +03:00
pr_warning ( " problem processing %d event, skipping it. \n " ,
event - > header . type ) ;
return - 1 ;
}
2012-10-05 18:44:46 +04:00
if ( al . filtered )
2009-12-16 01:04:41 +03:00
return 0 ;
2013-01-24 19:10:29 +04:00
if ( hists__add_entry ( & evsel - > hists , & al , sample - > period , sample - > weight ) ) {
2010-05-14 21:19:35 +04:00
pr_warning ( " problem incrementing symbol period, skipping event \n " ) ;
2009-12-15 01:09:31 +03:00
return - 1 ;
}
2012-09-06 19:46:55 +04:00
evsel - > hists . stats . total_period + = sample - > period ;
2009-12-15 01:09:31 +03:00
return 0 ;
}
2012-09-06 19:46:55 +04:00
static struct perf_tool tool = {
. sample = diff__process_sample_event ,
. mmap = perf_event__process_mmap ,
. comm = perf_event__process_comm ,
2012-10-06 22:44:59 +04:00
. exit = perf_event__process_exit ,
. fork = perf_event__process_fork ,
2012-09-06 19:46:55 +04:00
. lost = perf_event__process_lost ,
. ordered_samples = true ,
. ordering_requires_timestamps = true ,
2009-12-15 01:09:31 +03:00
} ;
2012-09-06 19:46:55 +04:00
static struct perf_evsel * evsel_match ( struct perf_evsel * evsel ,
struct perf_evlist * evlist )
{
struct perf_evsel * e ;
list_for_each_entry ( e , & evlist - > entries , node )
if ( perf_evsel__match2 ( evsel , e ) )
return e ;
return NULL ;
}
2012-12-10 12:29:55 +04:00
static void perf_evlist__collapse_resort ( struct perf_evlist * evlist )
2012-10-04 16:49:36 +04:00
{
struct perf_evsel * evsel ;
list_for_each_entry ( evsel , & evlist - > entries , node ) {
struct hists * hists = & evsel - > hists ;
2012-12-10 12:29:55 +04:00
hists__collapse_resort ( hists ) ;
2012-10-04 16:49:36 +04:00
}
}
2012-10-05 18:44:40 +04:00
static void hists__baseline_only ( struct hists * hists )
{
2012-12-10 12:29:55 +04:00
struct rb_root * root ;
struct rb_node * next ;
if ( sort__need_collapse )
root = & hists - > entries_collapsed ;
else
root = hists - > entries_in ;
2012-10-05 18:44:40 +04:00
2012-12-10 12:29:55 +04:00
next = rb_first ( root ) ;
2012-10-05 18:44:40 +04:00
while ( next ! = NULL ) {
2012-12-10 12:29:55 +04:00
struct hist_entry * he = rb_entry ( next , struct hist_entry , rb_node_in ) ;
2012-10-05 18:44:40 +04:00
2012-12-10 12:29:55 +04:00
next = rb_next ( & he - > rb_node_in ) ;
2012-10-25 20:42:45 +04:00
if ( ! hist_entry__next_pair ( he ) ) {
2012-12-10 12:29:55 +04:00
rb_erase ( & he - > rb_node_in , root ) ;
2012-10-05 18:44:40 +04:00
hist_entry__free ( he ) ;
}
}
}
2012-10-05 18:44:42 +04:00
static void hists__precompute ( struct hists * hists )
{
2012-12-13 17:08:59 +04:00
struct rb_root * root ;
struct rb_node * next ;
if ( sort__need_collapse )
root = & hists - > entries_collapsed ;
else
root = hists - > entries_in ;
2012-10-05 18:44:42 +04:00
2012-12-13 17:08:59 +04:00
next = rb_first ( root ) ;
2012-10-05 18:44:42 +04:00
while ( next ! = NULL ) {
2012-12-13 17:08:59 +04:00
struct hist_entry * he = rb_entry ( next , struct hist_entry , rb_node_in ) ;
2012-11-28 17:52:40 +04:00
struct hist_entry * pair = hist_entry__next_pair ( he ) ;
2012-10-05 18:44:42 +04:00
2012-12-13 17:08:59 +04:00
next = rb_next ( & he - > rb_node_in ) ;
2012-11-28 17:52:40 +04:00
if ( ! pair )
continue ;
2012-10-05 18:44:42 +04:00
switch ( compute ) {
case COMPUTE_DELTA :
2012-11-28 17:52:40 +04:00
perf_diff__compute_delta ( he , pair ) ;
2012-10-05 18:44:42 +04:00
break ;
case COMPUTE_RATIO :
2012-11-28 17:52:40 +04:00
perf_diff__compute_ratio ( he , pair ) ;
2012-10-05 18:44:42 +04:00
break ;
2012-10-05 18:44:43 +04:00
case COMPUTE_WEIGHTED_DIFF :
2012-11-28 17:52:40 +04:00
perf_diff__compute_wdiff ( he , pair ) ;
2012-10-05 18:44:43 +04:00
break ;
2012-10-05 18:44:42 +04:00
default :
BUG_ON ( 1 ) ;
}
}
}
static int64_t cmp_doubles ( double l , double r )
{
if ( l > r )
return - 1 ;
else if ( l < r )
return 1 ;
else
return 0 ;
}
static int64_t
hist_entry__cmp_compute ( struct hist_entry * left , struct hist_entry * right ,
int c )
{
switch ( c ) {
case COMPUTE_DELTA :
{
double l = left - > diff . period_ratio_delta ;
double r = right - > diff . period_ratio_delta ;
return cmp_doubles ( l , r ) ;
}
case COMPUTE_RATIO :
{
double l = left - > diff . period_ratio ;
double r = right - > diff . period_ratio ;
return cmp_doubles ( l , r ) ;
}
2012-10-05 18:44:43 +04:00
case COMPUTE_WEIGHTED_DIFF :
{
s64 l = left - > diff . wdiff ;
s64 r = right - > diff . wdiff ;
return r - l ;
}
2012-10-05 18:44:42 +04:00
default :
BUG_ON ( 1 ) ;
}
return 0 ;
}
static void insert_hist_entry_by_compute ( struct rb_root * root ,
struct hist_entry * he ,
int c )
{
struct rb_node * * p = & root - > rb_node ;
struct rb_node * parent = NULL ;
struct hist_entry * iter ;
while ( * p ! = NULL ) {
parent = * p ;
iter = rb_entry ( parent , struct hist_entry , rb_node ) ;
if ( hist_entry__cmp_compute ( he , iter , c ) < 0 )
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
}
rb_link_node ( & he - > rb_node , parent , p ) ;
rb_insert_color ( & he - > rb_node , root ) ;
}
static void hists__compute_resort ( struct hists * hists )
{
2012-12-10 12:29:56 +04:00
struct rb_root * root ;
struct rb_node * next ;
if ( sort__need_collapse )
root = & hists - > entries_collapsed ;
else
root = hists - > entries_in ;
hists - > entries = RB_ROOT ;
next = rb_first ( root ) ;
hists - > nr_entries = 0 ;
hists - > stats . total_period = 0 ;
hists__reset_col_len ( hists ) ;
2012-10-05 18:44:42 +04:00
while ( next ! = NULL ) {
2012-12-10 12:29:56 +04:00
struct hist_entry * he ;
2012-10-05 18:44:42 +04:00
2012-12-10 12:29:56 +04:00
he = rb_entry ( next , struct hist_entry , rb_node_in ) ;
next = rb_next ( & he - > rb_node_in ) ;
2012-10-05 18:44:42 +04:00
2012-12-10 12:29:56 +04:00
insert_hist_entry_by_compute ( & hists - > entries , he , compute ) ;
hists__inc_nr_entries ( hists , he ) ;
2012-10-05 18:44:42 +04:00
}
}
2012-10-05 18:44:40 +04:00
static void hists__process ( struct hists * old , struct hists * new )
{
2012-11-09 00:54:33 +04:00
hists__match ( new , old ) ;
2012-10-05 18:44:40 +04:00
if ( show_baseline_only )
hists__baseline_only ( new ) ;
2012-11-09 01:08:26 +04:00
else
hists__link ( new , old ) ;
2012-10-05 18:44:40 +04:00
2012-10-05 18:44:42 +04:00
if ( sort_compute ) {
hists__precompute ( new ) ;
hists__compute_resort ( new ) ;
2012-12-10 12:29:56 +04:00
} else {
hists__output_resort ( new ) ;
2012-10-05 18:44:42 +04:00
}
2013-05-14 06:09:04 +04:00
hists__fprintf ( new , true , 0 , 0 , 0 , stdout ) ;
2012-10-05 18:44:40 +04:00
}
2009-12-15 01:09:31 +03:00
static int __cmd_diff ( void )
{
int ret , i ;
2012-03-22 17:37:26 +04:00
# define older (session[0])
# define newer (session[1])
2009-12-15 01:09:31 +03:00
struct perf_session * session [ 2 ] ;
2012-09-06 19:46:55 +04:00
struct perf_evlist * evlist_new , * evlist_old ;
struct perf_evsel * evsel ;
bool first = true ;
2009-12-15 01:09:31 +03:00
2012-03-22 17:37:26 +04:00
older = perf_session__new ( input_old , O_RDONLY , force , false ,
2012-09-06 19:46:55 +04:00
& tool ) ;
2012-03-22 17:37:26 +04:00
newer = perf_session__new ( input_new , O_RDONLY , force , false ,
2012-09-06 19:46:55 +04:00
& tool ) ;
2009-12-15 01:09:31 +03:00
if ( session [ 0 ] = = NULL | | session [ 1 ] = = NULL )
return - ENOMEM ;
for ( i = 0 ; i < 2 ; + + i ) {
2012-09-06 19:46:55 +04:00
ret = perf_session__process_events ( session [ i ] , & tool ) ;
2009-12-15 01:09:31 +03:00
if ( ret )
goto out_delete ;
}
2012-09-06 19:46:55 +04:00
evlist_old = older - > evlist ;
evlist_new = newer - > evlist ;
2012-12-10 12:29:55 +04:00
perf_evlist__collapse_resort ( evlist_old ) ;
perf_evlist__collapse_resort ( evlist_new ) ;
2012-09-06 19:46:55 +04:00
list_for_each_entry ( evsel , & evlist_new - > entries , node ) {
struct perf_evsel * evsel_old ;
evsel_old = evsel_match ( evsel , evlist_old ) ;
if ( ! evsel_old )
continue ;
fprintf ( stdout , " %s# Event '%s' \n # \n " , first ? " " : " \n " ,
perf_evsel__name ( evsel ) ) ;
first = false ;
2012-10-05 18:44:40 +04:00
hists__process ( & evsel_old - > hists , & evsel - > hists ) ;
2012-09-06 19:46:55 +04:00
}
2009-12-29 03:48:36 +03:00
2009-12-15 01:09:31 +03:00
out_delete :
for ( i = 0 ; i < 2 ; + + i )
perf_session__delete ( session [ i ] ) ;
return ret ;
2012-03-22 17:37:26 +04:00
# undef older
# undef newer
2009-12-15 01:09:31 +03:00
}
2009-12-18 21:35:58 +03:00
static const char * const diff_usage [ ] = {
2009-12-15 01:09:31 +03:00
" perf diff [<options>] [old_file] [new_file] " ,
2009-12-18 21:35:58 +03:00
NULL ,
2009-12-15 01:09:31 +03:00
} ;
static const struct option options [ ] = {
2010-04-13 12:37:33 +04:00
OPT_INCR ( ' v ' , " verbose " , & verbose ,
2009-12-15 01:09:31 +03:00
" be more verbose (show symbol address, etc) " ) ,
2012-10-05 18:44:40 +04:00
OPT_BOOLEAN ( ' b ' , " baseline-only " , & show_baseline_only ,
" Show only items with match in baseline " ) ,
2012-10-05 18:44:43 +04:00
OPT_CALLBACK ( ' c ' , " compute " , & compute ,
" delta,ratio,wdiff:w1,w2 (default delta) " ,
2012-10-05 18:44:41 +04:00
" Entries differential computation selection " ,
setup_compute ) ,
2012-10-05 18:44:44 +04:00
OPT_BOOLEAN ( ' p ' , " period " , & show_period ,
" Show period values. " ) ,
2012-10-05 18:44:45 +04:00
OPT_BOOLEAN ( ' F ' , " formula " , & show_formula ,
" Show formula. " ) ,
2009-12-15 01:09:31 +03:00
OPT_BOOLEAN ( ' D ' , " dump-raw-trace " , & dump_trace ,
" dump raw trace in ASCII " ) ,
OPT_BOOLEAN ( ' f ' , " force " , & force , " don't complain, do it " ) ,
OPT_BOOLEAN ( ' m ' , " modules " , & symbol_conf . use_modules ,
" load module symbols - WARNING: use only with -k and LIVE kernel " ) ,
2009-12-16 01:04:41 +03:00
OPT_STRING ( ' d ' , " dsos " , & symbol_conf . dso_list_str , " dso[,dso...] " ,
" only consider symbols in these dsos " ) ,
OPT_STRING ( ' C ' , " comms " , & symbol_conf . comm_list_str , " comm[,comm...] " ,
" only consider symbols in these comms " ) ,
OPT_STRING ( ' S ' , " symbols " , & symbol_conf . sym_list_str , " symbol[,symbol...] " ,
" only consider these symbols " ) ,
perf diff: Use perf_session__fprintf_hists just like 'perf record'
That means that almost everything you can do with 'perf report'
can be done with 'perf diff', for instance:
$ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2699
samples) ] $ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2687
samples) ] perf diff | head -8
9.02% +1.00% find libc-2.10.1.so [.] _IO_vfprintf_internal
2.91% -1.00% find [kernel] [k] __kmalloc
2.85% -1.00% find [kernel] [k] ext4_htree_store_dirent
1.99% -1.00% find [kernel] [k] _atomic_dec_and_lock
2.44% find [kernel] [k] half_md4_transform
$
So if you want to zoom into libc:
$ perf diff --dsos libc-2.10.1.so | head -8
37.34% find [.] _IO_vfprintf_internal
10.34% find [.] __GI_memmove
8.25% +2.00% find [.] _int_malloc
5.07% -1.00% find [.] __GI_mempcpy
7.62% +2.00% find [.] _int_free
$
And if there were multiple commands using libc, it is also
possible to aggregate them all by using --sort symbol:
$ perf diff --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% [.] __GI_mempcpy
7.62% +2.00% [.] _int_free
$
The displacement column now is off by default, to use it:
perf diff -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% +2 [.] __GI_mempcpy
7.62% +2.00% -1 [.] _int_free
$
Using -t/--field-separator can be used for scripting:
$ perf diff -t, -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34, , ,[.] _IO_vfprintf_internal
10.34, , ,[.] __GI_memmove
8.25,+2.00%, ,[.] _int_malloc
5.07,-1.00%, +2,[.] __GI_mempcpy
7.62,+2.00%, -1,[.] _int_free
6.99,+1.00%, -1,[.] _IO_new_file_xsputn
1.89,-2.00%, +4,[.] __readdir64
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1260978567-550-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-12-16 18:49:27 +03:00
OPT_STRING ( ' s ' , " sort " , & sort_order , " key[,key2...] " ,
" sort by key(s): pid, comm, dso, symbol, parent " ) ,
OPT_STRING ( ' t ' , " field-separator " , & symbol_conf . field_sep , " separator " ,
" separator for columns, no spaces will be added between "
" columns '.' is reserved. " ) ,
2010-12-09 23:27:07 +03:00
OPT_STRING ( 0 , " symfs " , & symbol_conf . symfs , " directory " ,
" Look for files with symbols relative to this directory " ) ,
2009-12-15 01:09:31 +03:00
OPT_END ( )
} ;
2012-10-04 16:49:39 +04:00
static void ui_init ( void )
{
2012-10-05 18:44:45 +04:00
/*
2012-12-06 17:22:28 +04:00
* Display baseline / delta / ratio
2012-10-05 18:44:45 +04:00
* formula / periods columns .
*/
2012-10-13 02:06:16 +04:00
perf_hpp__column_enable ( PERF_HPP__BASELINE ) ;
2012-10-05 18:44:41 +04:00
switch ( compute ) {
case COMPUTE_DELTA :
2012-10-13 02:06:16 +04:00
perf_hpp__column_enable ( PERF_HPP__DELTA ) ;
2012-10-05 18:44:41 +04:00
break ;
case COMPUTE_RATIO :
2012-10-13 02:06:16 +04:00
perf_hpp__column_enable ( PERF_HPP__RATIO ) ;
2012-10-05 18:44:43 +04:00
break ;
case COMPUTE_WEIGHTED_DIFF :
2012-10-13 02:06:16 +04:00
perf_hpp__column_enable ( PERF_HPP__WEIGHTED_DIFF ) ;
2012-10-05 18:44:41 +04:00
break ;
default :
BUG_ON ( 1 ) ;
} ;
2012-10-04 16:49:39 +04:00
2012-10-05 18:44:45 +04:00
if ( show_formula )
2012-10-13 02:06:16 +04:00
perf_hpp__column_enable ( PERF_HPP__FORMULA ) ;
2012-10-05 18:44:45 +04:00
2012-10-05 18:44:44 +04:00
if ( show_period ) {
2012-10-13 02:06:16 +04:00
perf_hpp__column_enable ( PERF_HPP__PERIOD ) ;
perf_hpp__column_enable ( PERF_HPP__PERIOD_BASELINE ) ;
2012-10-05 18:44:44 +04:00
}
2012-10-04 16:49:39 +04:00
}
2012-09-11 02:15:03 +04:00
int cmd_diff ( int argc , const char * * argv , const char * prefix __maybe_unused )
2009-12-15 01:09:31 +03:00
{
2009-12-16 19:09:53 +03:00
sort_order = diff__default_sort_order ;
2009-12-15 01:09:31 +03:00
argc = parse_options ( argc , argv , options , diff_usage , 0 ) ;
if ( argc ) {
if ( argc > 2 )
usage_with_options ( diff_usage , options ) ;
if ( argc = = 2 ) {
input_old = argv [ 0 ] ;
input_new = argv [ 1 ] ;
} else
input_new = argv [ 0 ] ;
2010-04-19 09:32:50 +04:00
} else if ( symbol_conf . default_guest_vmlinux_name | |
symbol_conf . default_guest_kallsyms ) {
input_old = " perf.data.host " ;
input_new = " perf.data.guest " ;
2009-12-15 01:09:31 +03:00
}
perf diff: Use perf_session__fprintf_hists just like 'perf record'
That means that almost everything you can do with 'perf report'
can be done with 'perf diff', for instance:
$ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2699
samples) ] $ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2687
samples) ] perf diff | head -8
9.02% +1.00% find libc-2.10.1.so [.] _IO_vfprintf_internal
2.91% -1.00% find [kernel] [k] __kmalloc
2.85% -1.00% find [kernel] [k] ext4_htree_store_dirent
1.99% -1.00% find [kernel] [k] _atomic_dec_and_lock
2.44% find [kernel] [k] half_md4_transform
$
So if you want to zoom into libc:
$ perf diff --dsos libc-2.10.1.so | head -8
37.34% find [.] _IO_vfprintf_internal
10.34% find [.] __GI_memmove
8.25% +2.00% find [.] _int_malloc
5.07% -1.00% find [.] __GI_mempcpy
7.62% +2.00% find [.] _int_free
$
And if there were multiple commands using libc, it is also
possible to aggregate them all by using --sort symbol:
$ perf diff --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% [.] __GI_mempcpy
7.62% +2.00% [.] _int_free
$
The displacement column now is off by default, to use it:
perf diff -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% +2 [.] __GI_mempcpy
7.62% +2.00% -1 [.] _int_free
$
Using -t/--field-separator can be used for scripting:
$ perf diff -t, -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34, , ,[.] _IO_vfprintf_internal
10.34, , ,[.] __GI_memmove
8.25,+2.00%, ,[.] _int_malloc
5.07,-1.00%, +2,[.] __GI_mempcpy
7.62,+2.00%, -1,[.] _int_free
6.99,+1.00%, -1,[.] _IO_new_file_xsputn
1.89,-2.00%, +4,[.] __readdir64
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1260978567-550-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-12-16 18:49:27 +03:00
symbol_conf . exclude_other = false ;
2009-12-16 01:04:40 +03:00
if ( symbol__init ( ) < 0 )
return - 1 ;
2012-10-04 16:49:39 +04:00
ui_init ( ) ;
2013-02-06 09:57:16 +04:00
if ( setup_sorting ( ) < 0 )
usage_with_options ( diff_usage , options ) ;
2009-12-15 01:09:31 +03:00
setup_pager ( ) ;
perf diff: Use perf_session__fprintf_hists just like 'perf record'
That means that almost everything you can do with 'perf report'
can be done with 'perf diff', for instance:
$ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2699
samples) ] $ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2687
samples) ] perf diff | head -8
9.02% +1.00% find libc-2.10.1.so [.] _IO_vfprintf_internal
2.91% -1.00% find [kernel] [k] __kmalloc
2.85% -1.00% find [kernel] [k] ext4_htree_store_dirent
1.99% -1.00% find [kernel] [k] _atomic_dec_and_lock
2.44% find [kernel] [k] half_md4_transform
$
So if you want to zoom into libc:
$ perf diff --dsos libc-2.10.1.so | head -8
37.34% find [.] _IO_vfprintf_internal
10.34% find [.] __GI_memmove
8.25% +2.00% find [.] _int_malloc
5.07% -1.00% find [.] __GI_mempcpy
7.62% +2.00% find [.] _int_free
$
And if there were multiple commands using libc, it is also
possible to aggregate them all by using --sort symbol:
$ perf diff --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% [.] __GI_mempcpy
7.62% +2.00% [.] _int_free
$
The displacement column now is off by default, to use it:
perf diff -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% +2 [.] __GI_mempcpy
7.62% +2.00% -1 [.] _int_free
$
Using -t/--field-separator can be used for scripting:
$ perf diff -t, -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34, , ,[.] _IO_vfprintf_internal
10.34, , ,[.] __GI_memmove
8.25,+2.00%, ,[.] _int_malloc
5.07,-1.00%, +2,[.] __GI_mempcpy
7.62,+2.00%, -1,[.] _int_free
6.99,+1.00%, -1,[.] _IO_new_file_xsputn
1.89,-2.00%, +4,[.] __readdir64
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1260978567-550-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-12-16 18:49:27 +03:00
2013-04-03 16:26:19 +04:00
sort__setup_elide ( NULL ) ;
perf diff: Use perf_session__fprintf_hists just like 'perf record'
That means that almost everything you can do with 'perf report'
can be done with 'perf diff', for instance:
$ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2699
samples) ] $ perf record -f find / > /dev/null
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.062 MB perf.data (~2687
samples) ] perf diff | head -8
9.02% +1.00% find libc-2.10.1.so [.] _IO_vfprintf_internal
2.91% -1.00% find [kernel] [k] __kmalloc
2.85% -1.00% find [kernel] [k] ext4_htree_store_dirent
1.99% -1.00% find [kernel] [k] _atomic_dec_and_lock
2.44% find [kernel] [k] half_md4_transform
$
So if you want to zoom into libc:
$ perf diff --dsos libc-2.10.1.so | head -8
37.34% find [.] _IO_vfprintf_internal
10.34% find [.] __GI_memmove
8.25% +2.00% find [.] _int_malloc
5.07% -1.00% find [.] __GI_mempcpy
7.62% +2.00% find [.] _int_free
$
And if there were multiple commands using libc, it is also
possible to aggregate them all by using --sort symbol:
$ perf diff --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% [.] __GI_mempcpy
7.62% +2.00% [.] _int_free
$
The displacement column now is off by default, to use it:
perf diff -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34% [.] _IO_vfprintf_internal
10.34% [.] __GI_memmove
8.25% +2.00% [.] _int_malloc
5.07% -1.00% +2 [.] __GI_mempcpy
7.62% +2.00% -1 [.] _int_free
$
Using -t/--field-separator can be used for scripting:
$ perf diff -t, -m --dsos libc-2.10.1.so --sort symbol | head -8
37.34, , ,[.] _IO_vfprintf_internal
10.34, , ,[.] __GI_memmove
8.25,+2.00%, ,[.] _int_malloc
5.07,-1.00%, +2,[.] __GI_mempcpy
7.62,+2.00%, -1,[.] _int_free
6.99,+1.00%, -1,[.] _IO_new_file_xsputn
1.89,-2.00%, +4,[.] __readdir64
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1260978567-550-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-12-16 18:49:27 +03:00
2009-12-15 01:09:31 +03:00
return __cmd_diff ( ) ;
}