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>
2013-02-03 23:08:34 +04:00
# include <math.h>
/* Diff command specific HPP columns. */
enum {
PERF_HPP_DIFF__BASELINE ,
PERF_HPP_DIFF__PERIOD ,
PERF_HPP_DIFF__PERIOD_BASELINE ,
PERF_HPP_DIFF__DELTA ,
PERF_HPP_DIFF__RATIO ,
PERF_HPP_DIFF__WEIGHTED_DIFF ,
PERF_HPP_DIFF__FORMULA ,
PERF_HPP_DIFF__MAX_INDEX
} ;
struct diff_hpp_fmt {
struct perf_hpp_fmt fmt ;
int idx ;
char * header ;
int header_width ;
} ;
2009-12-15 01:09:31 +03:00
2013-03-25 03:02:01 +04:00
struct data__file {
struct perf_session * session ;
const char * file ;
int idx ;
2012-12-02 01:00:00 +04:00
struct hists * hists ;
2012-12-02 00:57:04 +04:00
struct diff_hpp_fmt fmt [ PERF_HPP_DIFF__MAX_INDEX ] ;
2013-03-25 03:02:01 +04:00
} ;
static struct data__file * data__files ;
static int data__files_cnt ;
# define data__for_each_file_start(i, d, s) \
for ( i = s , d = & data__files [ s ] ; \
i < data__files_cnt ; \
i + + , d = & data__files [ i ] )
# define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
2012-12-02 01:00:00 +04:00
# define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
2013-03-25 03:02:01 +04:00
static char diff__default_sort_order [ ] = " dso,symbol " ;
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 ;
2013-02-03 23:08:34 +04:00
static int compute_2_hpp [ COMPUTE_MAX ] = {
[ COMPUTE_DELTA ] = PERF_HPP_DIFF__DELTA ,
[ COMPUTE_RATIO ] = PERF_HPP_DIFF__RATIO ,
[ COMPUTE_WEIGHTED_DIFF ] = PERF_HPP_DIFF__WEIGHTED_DIFF ,
} ;
# define MAX_COL_WIDTH 70
static struct header_column {
const char * name ;
int width ;
} columns [ PERF_HPP_DIFF__MAX_INDEX ] = {
[ PERF_HPP_DIFF__BASELINE ] = {
. name = " Baseline " ,
} ,
[ PERF_HPP_DIFF__PERIOD ] = {
. name = " Period " ,
. width = 14 ,
} ,
[ PERF_HPP_DIFF__PERIOD_BASELINE ] = {
. name = " Base period " ,
. width = 14 ,
} ,
[ PERF_HPP_DIFF__DELTA ] = {
. name = " Delta " ,
. width = 7 ,
} ,
[ PERF_HPP_DIFF__RATIO ] = {
. name = " Ratio " ,
. width = 14 ,
} ,
[ PERF_HPP_DIFF__WEIGHTED_DIFF ] = {
. name = " Weighted diff " ,
. width = 14 ,
} ,
[ PERF_HPP_DIFF__FORMULA ] = {
. name = " Formula " ,
. width = MAX_COL_WIDTH ,
}
} ;
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-10-22 01:31:51 +04:00
static double 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-10-22 01:31:51 +04:00
static double compute_delta ( struct hist_entry * he , struct hist_entry * pair )
2012-10-05 18:44:42 +04:00
{
2012-10-22 01:31:51 +04:00
double old_percent = period_percent ( he , he - > stat . period ) ;
double new_percent = period_percent ( pair , pair - > stat . period ) ;
2012-10-05 18:44:42 +04:00
2012-12-02 00:15:40 +04:00
pair - > diff . period_ratio_delta = new_percent - old_percent ;
pair - > diff . computed = true ;
return pair - > diff . period_ratio_delta ;
2012-10-05 18:44:42 +04:00
}
2012-10-22 01:31:51 +04:00
static double compute_ratio ( struct hist_entry * he , struct hist_entry * pair )
2012-10-05 18:44:42 +04:00
{
2012-12-02 00:15:40 +04:00
double old_period = he - > stat . period ? : 1 ;
double new_period = pair - > stat . period ;
2012-10-05 18:44:42 +04:00
2012-12-02 00:15:40 +04:00
pair - > diff . computed = true ;
pair - > diff . period_ratio = new_period / old_period ;
return pair - > diff . period_ratio ;
2012-10-05 18:44:42 +04:00
}
2012-10-22 01:31:51 +04:00
static s64 compute_wdiff ( struct hist_entry * he , struct hist_entry * pair )
2012-10-05 18:44:43 +04:00
{
2012-12-02 00:15:40 +04:00
u64 old_period = he - > stat . period ;
u64 new_period = pair - > stat . period ;
2012-10-05 18:44:43 +04:00
2012-12-02 00:15:40 +04:00
pair - > diff . computed = true ;
pair - > diff . wdiff = new_period * compute_wdiff_w2 -
old_period * compute_wdiff_w1 ;
2012-10-05 18:44:43 +04:00
2012-12-02 00:15:40 +04:00
return pair - > diff . wdiff ;
2012-10-05 18:44:43 +04:00
}
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 " ) " ,
2012-12-02 00:15:40 +04:00
pair - > stat . period , pair - > hists - > stats . total_period ,
he - > stat . period , he - > hists - > stats . total_period ) ;
2012-10-05 18:44:45 +04:00
}
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
{
2012-12-02 00:15:40 +04:00
double old_period = he - > stat . period ;
double new_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
{
2012-12-02 00:15:40 +04:00
u64 old_period = he - > stat . period ;
u64 new_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-10-22 01:31:51 +04:00
static int formula_fprintf ( 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-10-22 01:31:51 +04:00
compute_delta ( he , pair ) ;
2012-10-05 18:44:42 +04:00
break ;
case COMPUTE_RATIO :
2012-10-22 01:31:51 +04:00
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-10-22 01:31:51 +04:00
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-12-02 01:00:00 +04:00
static void hists__process ( struct hists * hists )
2012-10-05 18:44:40 +04:00
{
if ( show_baseline_only )
2012-12-02 01:00:00 +04:00
hists__baseline_only ( hists ) ;
2012-10-05 18:44:40 +04:00
2012-10-05 18:44:42 +04:00
if ( sort_compute ) {
2012-12-02 01:00:00 +04:00
hists__precompute ( hists ) ;
hists__compute_resort ( hists ) ;
2012-12-10 12:29:56 +04:00
} else {
2012-12-02 01:00:00 +04:00
hists__output_resort ( hists ) ;
2012-10-05 18:44:42 +04:00
}
2012-12-02 01:00:00 +04:00
hists__fprintf ( hists , true , 0 , 0 , 0 , stdout ) ;
2012-10-05 18:44:40 +04:00
}
2012-12-02 00:56:03 +04:00
static void data__fprintf ( void )
{
struct data__file * d ;
int i ;
fprintf ( stdout , " # Data files: \n " ) ;
data__for_each_file ( i , d )
fprintf ( stdout , " # [%d] %s %s \n " ,
d - > idx , d - > file ,
! d - > idx ? " (Baseline) " : " " ) ;
fprintf ( stdout , " # \n " ) ;
}
2013-03-25 03:02:01 +04:00
static void data_process ( void )
2009-12-15 01:09:31 +03:00
{
2012-12-02 01:00:00 +04:00
struct perf_evlist * evlist_base = data__files [ 0 ] . session - > evlist ;
struct perf_evsel * evsel_base ;
2012-09-06 19:46:55 +04:00
bool first = true ;
2009-12-15 01:09:31 +03:00
2012-12-02 01:00:00 +04:00
list_for_each_entry ( evsel_base , & evlist_base - > entries , node ) {
struct data__file * d ;
int i ;
2009-12-15 01:09:31 +03:00
2012-12-02 01:00:00 +04:00
data__for_each_file_new ( i , d ) {
struct perf_evlist * evlist = d - > session - > evlist ;
struct perf_evsel * evsel ;
evsel = evsel_match ( evsel_base , evlist ) ;
if ( ! evsel )
continue ;
d - > hists = & evsel - > hists ;
hists__match ( & evsel_base - > hists , & evsel - > hists ) ;
if ( ! show_baseline_only )
hists__link ( & evsel_base - > hists ,
& evsel - > hists ) ;
}
2009-12-15 01:09:31 +03:00
2013-03-25 03:02:01 +04:00
fprintf ( stdout , " %s# Event '%s' \n # \n " , first ? " " : " \n " ,
2012-12-02 01:00:00 +04:00
perf_evsel__name ( evsel_base ) ) ;
2012-09-06 19:46:55 +04:00
2013-03-25 03:02:01 +04:00
first = false ;
2012-09-06 19:46:55 +04:00
2012-12-02 01:00:00 +04:00
if ( verbose | | data__files_cnt > 2 )
2012-12-02 00:56:03 +04:00
data__fprintf ( ) ;
2012-12-02 01:00:00 +04:00
hists__process ( & evsel_base - > hists ) ;
2013-03-25 03:02:01 +04:00
}
}
2012-09-06 19:46:55 +04:00
2012-12-02 00:57:04 +04:00
static void data__free ( struct data__file * d )
{
int col ;
for ( col = 0 ; col < PERF_HPP_DIFF__MAX_INDEX ; col + + ) {
struct diff_hpp_fmt * fmt = & d - > fmt [ col ] ;
free ( fmt - > header ) ;
}
}
2013-03-25 03:02:01 +04:00
static int __cmd_diff ( void )
{
struct data__file * d ;
int ret = - EINVAL , i ;
data__for_each_file ( i , d ) {
d - > session = perf_session__new ( d - > file , O_RDONLY , force ,
false , & tool ) ;
if ( ! d - > session ) {
pr_err ( " Failed to open %s \n " , d - > file ) ;
ret = - ENOMEM ;
goto out_delete ;
}
2012-09-06 19:46:55 +04:00
2013-03-25 03:02:01 +04:00
ret = perf_session__process_events ( d - > session , & tool ) ;
if ( ret ) {
pr_err ( " Failed to process %s \n " , d - > file ) ;
goto out_delete ;
}
2012-09-06 19:46:55 +04:00
2013-03-25 03:02:01 +04:00
perf_evlist__collapse_resort ( d - > session - > evlist ) ;
}
data_process ( ) ;
2012-09-06 19:46:55 +04:00
2013-03-25 03:02:01 +04:00
out_delete :
data__for_each_file ( i , d ) {
if ( d - > session )
perf_session__delete ( d - > session ) ;
2012-12-02 00:57:04 +04:00
data__free ( d ) ;
2012-09-06 19:46:55 +04:00
}
2009-12-29 03:48:36 +03:00
2013-03-25 03:02:01 +04:00
free ( data__files ) ;
2009-12-15 01:09:31 +03:00
return ret ;
}
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 ( )
} ;
2013-02-03 23:08:34 +04:00
static double baseline_percent ( struct hist_entry * he )
2012-10-04 16:49:39 +04:00
{
2013-02-03 23:08:34 +04:00
struct hists * hists = he - > hists ;
return 100.0 * he - > stat . period / hists - > stats . total_period ;
}
2012-10-05 18:44:41 +04:00
2013-02-03 23:08:34 +04:00
static int hpp__color_baseline ( struct perf_hpp_fmt * fmt ,
struct perf_hpp * hpp , struct hist_entry * he )
{
struct diff_hpp_fmt * dfmt =
container_of ( fmt , struct diff_hpp_fmt , fmt ) ;
double percent = baseline_percent ( he ) ;
char pfmt [ 20 ] = " " ;
if ( ! he - > dummy ) {
scnprintf ( pfmt , 20 , " %%%d.2f%%%% " , dfmt - > header_width - 1 ) ;
return percent_color_snprintf ( hpp - > buf , hpp - > size ,
pfmt , percent ) ;
} else
return scnprintf ( hpp - > buf , hpp - > size , " %*s " ,
dfmt - > header_width , pfmt ) ;
}
static int hpp__entry_baseline ( struct hist_entry * he , char * buf , size_t size )
{
double percent = baseline_percent ( he ) ;
const char * fmt = symbol_conf . field_sep ? " %.2f " : " %6.2f%% " ;
int ret = 0 ;
if ( ! he - > dummy )
ret = scnprintf ( buf , size , fmt , percent ) ;
return ret ;
}
static void
hpp__entry_unpair ( struct hist_entry * he , int idx , char * buf , size_t size )
{
switch ( idx ) {
case PERF_HPP_DIFF__PERIOD_BASELINE :
scnprintf ( buf , size , " % " PRIu64 , he - > stat . period ) ;
2012-10-05 18:44:41 +04:00
break ;
2013-02-03 23:08:34 +04:00
default :
2012-10-05 18:44:43 +04:00
break ;
2013-02-03 23:08:34 +04:00
}
}
static void
hpp__entry_pair ( struct hist_entry * he , struct hist_entry * pair ,
int idx , char * buf , size_t size )
{
double diff ;
double ratio ;
s64 wdiff ;
switch ( idx ) {
case PERF_HPP_DIFF__DELTA :
if ( pair - > diff . computed )
diff = pair - > diff . period_ratio_delta ;
else
2012-10-22 01:31:51 +04:00
diff = compute_delta ( he , pair ) ;
2013-02-03 23:08:34 +04:00
if ( fabs ( diff ) > = 0.01 )
scnprintf ( buf , size , " %+4.2F%% " , diff ) ;
break ;
case PERF_HPP_DIFF__RATIO :
/* No point for ratio number if we are dummy.. */
if ( he - > dummy )
break ;
if ( pair - > diff . computed )
ratio = pair - > diff . period_ratio ;
else
2012-10-22 01:31:51 +04:00
ratio = compute_ratio ( he , pair ) ;
2013-02-03 23:08:34 +04:00
if ( ratio > 0.0 )
scnprintf ( buf , size , " %14.6F " , ratio ) ;
break ;
case PERF_HPP_DIFF__WEIGHTED_DIFF :
/* No point for wdiff number if we are dummy.. */
if ( he - > dummy )
break ;
if ( pair - > diff . computed )
wdiff = pair - > diff . wdiff ;
else
2012-10-22 01:31:51 +04:00
wdiff = compute_wdiff ( he , pair ) ;
2013-02-03 23:08:34 +04:00
if ( wdiff ! = 0 )
scnprintf ( buf , size , " %14ld " , wdiff ) ;
break ;
case PERF_HPP_DIFF__FORMULA :
2012-10-22 01:31:51 +04:00
formula_fprintf ( he , pair , buf , size ) ;
2012-10-05 18:44:41 +04:00
break ;
2013-02-03 23:08:34 +04:00
case PERF_HPP_DIFF__PERIOD :
scnprintf ( buf , size , " % " PRIu64 , pair - > stat . period ) ;
break ;
2012-10-05 18:44:41 +04:00
default :
BUG_ON ( 1 ) ;
} ;
2013-02-03 23:08:34 +04:00
}
2012-12-02 01:00:00 +04:00
static struct hist_entry * get_pair ( struct hist_entry * he ,
struct diff_hpp_fmt * dfmt )
{
void * ptr = dfmt - dfmt - > idx ;
struct data__file * d = container_of ( ptr , struct data__file , fmt ) ;
if ( hist_entry__has_pairs ( he ) ) {
struct hist_entry * pair ;
list_for_each_entry ( pair , & he - > pairs . head , pairs . node )
if ( pair - > hists = = d - > hists )
return pair ;
}
return NULL ;
}
2013-02-03 23:08:34 +04:00
static void
2012-12-02 01:00:00 +04:00
__hpp__entry_global ( struct hist_entry * he , struct diff_hpp_fmt * dfmt ,
char * buf , size_t size )
2013-02-03 23:08:34 +04:00
{
2012-12-02 01:00:00 +04:00
struct hist_entry * pair = get_pair ( he , dfmt ) ;
int idx = dfmt - > idx ;
2013-02-03 23:08:34 +04:00
/* baseline is special */
if ( idx = = PERF_HPP_DIFF__BASELINE )
hpp__entry_baseline ( he , buf , size ) ;
else {
if ( pair )
hpp__entry_pair ( he , pair , idx , buf , size ) ;
else
hpp__entry_unpair ( he , idx , buf , size ) ;
}
}
static int hpp__entry_global ( struct perf_hpp_fmt * _fmt , struct perf_hpp * hpp ,
struct hist_entry * he )
{
struct diff_hpp_fmt * dfmt =
container_of ( _fmt , struct diff_hpp_fmt , fmt ) ;
char buf [ MAX_COL_WIDTH ] = " " ;
2012-12-02 01:00:00 +04:00
__hpp__entry_global ( he , dfmt , buf , MAX_COL_WIDTH ) ;
2013-02-03 23:08:34 +04:00
if ( symbol_conf . field_sep )
return scnprintf ( hpp - > buf , hpp - > size , " %s " , buf ) ;
else
return scnprintf ( hpp - > buf , hpp - > size , " %*s " ,
dfmt - > header_width , buf ) ;
}
static int hpp__header ( struct perf_hpp_fmt * fmt ,
struct perf_hpp * hpp )
{
struct diff_hpp_fmt * dfmt =
container_of ( fmt , struct diff_hpp_fmt , fmt ) ;
BUG_ON ( ! dfmt - > header ) ;
return scnprintf ( hpp - > buf , hpp - > size , dfmt - > header ) ;
}
static int hpp__width ( struct perf_hpp_fmt * fmt ,
struct perf_hpp * hpp __maybe_unused )
{
struct diff_hpp_fmt * dfmt =
container_of ( fmt , struct diff_hpp_fmt , fmt ) ;
BUG_ON ( dfmt - > header_width < = 0 ) ;
return dfmt - > header_width ;
}
2012-12-02 01:00:00 +04:00
static void init_header ( struct data__file * d , struct diff_hpp_fmt * dfmt )
2013-02-03 23:08:34 +04:00
{
# define MAX_HEADER_NAME 100
char buf_indent [ MAX_HEADER_NAME ] ;
char buf [ MAX_HEADER_NAME ] ;
const char * header = NULL ;
int width = 0 ;
BUG_ON ( dfmt - > idx > = PERF_HPP_DIFF__MAX_INDEX ) ;
header = columns [ dfmt - > idx ] . name ;
width = columns [ dfmt - > idx ] . width ;
/* Only our defined HPP fmts should appear here. */
BUG_ON ( ! header ) ;
2012-12-02 01:00:00 +04:00
if ( data__files_cnt > 2 )
scnprintf ( buf , MAX_HEADER_NAME , " %s/%d " , header , d - > idx ) ;
2013-02-03 23:08:34 +04:00
# define NAME (data__files_cnt > 2 ? buf : header)
dfmt - > header_width = width ;
width = ( int ) strlen ( NAME ) ;
if ( dfmt - > header_width < width )
dfmt - > header_width = width ;
scnprintf ( buf_indent , MAX_HEADER_NAME , " %*s " ,
dfmt - > header_width , NAME ) ;
dfmt - > header = strdup ( buf_indent ) ;
# undef MAX_HEADER_NAME
# undef NAME
}
2012-12-02 00:57:04 +04:00
static void data__hpp_register ( struct data__file * d , int idx )
2013-02-03 23:08:34 +04:00
{
2012-12-02 00:57:04 +04:00
struct diff_hpp_fmt * dfmt = & d - > fmt [ idx ] ;
struct perf_hpp_fmt * fmt = & dfmt - > fmt ;
dfmt - > idx = idx ;
fmt - > header = hpp__header ;
fmt - > width = hpp__width ;
fmt - > entry = hpp__entry_global ;
/* TODO more colors */
if ( idx = = PERF_HPP_DIFF__BASELINE )
fmt - > color = hpp__color_baseline ;
2013-02-03 23:08:34 +04:00
2012-12-02 01:00:00 +04:00
init_header ( d , dfmt ) ;
2012-12-02 00:57:04 +04:00
perf_hpp__column_register ( fmt ) ;
2013-02-03 23:08:34 +04:00
}
static void ui_init ( void )
{
2012-12-02 00:57:04 +04:00
struct data__file * d ;
int i ;
data__for_each_file ( i , d ) {
/*
* Baseline or compute realted columns :
*
* PERF_HPP_DIFF__BASELINE
* PERF_HPP_DIFF__DELTA
* PERF_HPP_DIFF__RATIO
* PERF_HPP_DIFF__WEIGHTED_DIFF
*/
data__hpp_register ( d , i ? compute_2_hpp [ compute ] :
PERF_HPP_DIFF__BASELINE ) ;
2012-10-04 16:49:39 +04:00
2012-12-02 00:57:04 +04:00
/*
* And the rest :
*
* PERF_HPP_DIFF__FORMULA
* PERF_HPP_DIFF__PERIOD
* PERF_HPP_DIFF__PERIOD_BASELINE
*/
if ( show_formula & & i )
data__hpp_register ( d , PERF_HPP_DIFF__FORMULA ) ;
2012-10-05 18:44:45 +04:00
2012-12-02 00:57:04 +04:00
if ( show_period )
data__hpp_register ( d , i ? PERF_HPP_DIFF__PERIOD :
PERF_HPP_DIFF__PERIOD_BASELINE ) ;
2012-10-05 18:44:44 +04:00
}
2012-10-04 16:49:39 +04:00
}
2013-03-25 03:02:01 +04:00
static int data_init ( int argc , const char * * argv )
2009-12-15 01:09:31 +03:00
{
2013-03-25 03:02:01 +04:00
struct data__file * d ;
static const char * defaults [ ] = {
" perf.data.old " ,
" perf.data " ,
} ;
2012-12-02 01:00:00 +04:00
bool use_default = true ;
2013-03-25 03:02:01 +04:00
int i ;
data__files_cnt = 2 ;
2009-12-15 01:09:31 +03:00
if ( argc ) {
2012-12-02 01:00:00 +04:00
if ( argc = = 1 )
2013-03-25 03:02:01 +04:00
defaults [ 1 ] = argv [ 0 ] ;
2012-12-02 01:00:00 +04:00
else {
data__files_cnt = argc ;
use_default = false ;
}
2010-04-19 09:32:50 +04:00
} else if ( symbol_conf . default_guest_vmlinux_name | |
symbol_conf . default_guest_kallsyms ) {
2013-03-25 03:02:01 +04:00
defaults [ 0 ] = " perf.data.host " ;
defaults [ 1 ] = " perf.data.guest " ;
2009-12-15 01:09:31 +03:00
}
2013-03-25 03:02:01 +04:00
data__files = zalloc ( sizeof ( * data__files ) * data__files_cnt ) ;
if ( ! data__files )
return - ENOMEM ;
data__for_each_file ( i , d ) {
2012-12-02 01:00:00 +04:00
d - > file = use_default ? defaults [ i ] : argv [ i ] ;
2013-03-25 03:02:01 +04:00
d - > idx = i ;
}
return 0 ;
}
int cmd_diff ( int argc , const char * * argv , const char * prefix __maybe_unused )
{
sort_order = diff__default_sort_order ;
argc = parse_options ( argc , argv , options , diff_usage , 0 ) ;
2009-12-16 01:04:40 +03:00
if ( symbol__init ( ) < 0 )
return - 1 ;
2013-03-25 03:02:01 +04:00
if ( data_init ( argc , argv ) < 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 ( ) ;
}