2009-12-14 20:09:31 -02: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 07:56:39 -02:00
# include "util/evsel.h"
2012-09-06 17:46:55 +02:00
# include "util/evlist.h"
2009-12-14 20:09:31 -02:00
# include "util/session.h"
2011-11-28 08:30:20 -02:00
# include "util/tool.h"
2009-12-14 20:09:31 -02:00
# include "util/sort.h"
# include "util/symbol.h"
# include "util/util.h"
2013-10-15 16:27:32 +02:00
# include "util/data.h"
2009-12-14 20:09:31 -02:00
# include <stdlib.h>
2013-02-03 20:08:34 +01: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-14 20:09:31 -02:00
2013-03-25 00:02:01 +01:00
struct data__file {
struct perf_session * session ;
2013-10-15 16:27:32 +02:00
struct perf_data_file file ;
2013-03-25 00:02:01 +01:00
int idx ;
2012-12-01 22:00:00 +01:00
struct hists * hists ;
2012-12-01 21:57:04 +01:00
struct diff_hpp_fmt fmt [ PERF_HPP_DIFF__MAX_INDEX ] ;
2013-03-25 00:02:01 +01: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-01 22:00:00 +01:00
# define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
2013-03-25 00:02:01 +01:00
static bool force ;
2012-10-05 16:44:44 +02:00
static bool show_period ;
2012-10-05 16:44:45 +02:00
static bool show_formula ;
2012-10-05 16:44:40 +02:00
static bool show_baseline_only ;
2012-11-25 23:10:20 +01:00
static unsigned int sort_compute ;
2009-12-14 20:09:31 -02:00
2012-10-05 16:44:43 +02:00
static s64 compute_wdiff_w1 ;
static s64 compute_wdiff_w2 ;
2012-10-05 16:44:41 +02:00
enum {
COMPUTE_DELTA ,
COMPUTE_RATIO ,
2012-10-05 16:44:43 +02:00
COMPUTE_WEIGHTED_DIFF ,
2012-10-05 16:44:41 +02:00
COMPUTE_MAX ,
} ;
const char * compute_names [ COMPUTE_MAX ] = {
[ COMPUTE_DELTA ] = " delta " ,
[ COMPUTE_RATIO ] = " ratio " ,
2012-10-05 16:44:43 +02:00
[ COMPUTE_WEIGHTED_DIFF ] = " wdiff " ,
2012-10-05 16:44:41 +02:00
} ;
static int compute ;
2013-02-03 20:08:34 +01: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 16:44:43 +02: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 16:44:41 +02:00
static int setup_compute ( const struct option * opt , const char * str ,
int unset __maybe_unused )
{
int * cp = ( int * ) opt - > value ;
2012-10-05 16:44:43 +02:00
char * cstr = ( char * ) str ;
char buf [ 50 ] ;
2012-10-05 16:44:41 +02:00
unsigned i ;
2012-10-05 16:44:43 +02:00
char * option ;
2012-10-05 16:44:41 +02:00
if ( ! str ) {
* cp = COMPUTE_DELTA ;
return 0 ;
}
2012-10-05 16:44:43 +02: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 16:44:41 +02:00
for ( i = 0 ; i < COMPUTE_MAX ; i + + )
2012-10-05 16:44:43 +02:00
if ( ! strcmp ( cstr , compute_names [ i ] ) ) {
2012-10-05 16:44:41 +02:00
* cp = i ;
2012-10-05 16:44:43 +02:00
return setup_compute_opt ( option ) ;
2012-10-05 16:44:41 +02:00
}
pr_err ( " Failed: '%s' is not computation method "
2012-10-05 16:44:43 +02:00
" (use 'delta','ratio' or 'wdiff') \n " , str ) ;
2012-10-05 16:44:41 +02:00
return - EINVAL ;
}
2012-10-21 23:31:51 +02:00
static double period_percent ( struct hist_entry * he , u64 period )
2012-10-05 16:44:42 +02:00
{
2014-02-07 12:06:07 +09:00
u64 total = hists__total_period ( he - > hists ) ;
2012-10-05 16:44:42 +02:00
return ( period * 100.0 ) / total ;
}
2012-10-21 23:31:51 +02:00
static double compute_delta ( struct hist_entry * he , struct hist_entry * pair )
2012-10-05 16:44:42 +02:00
{
2012-10-21 23:31:51 +02:00
double old_percent = period_percent ( he , he - > stat . period ) ;
double new_percent = period_percent ( pair , pair - > stat . period ) ;
2012-10-05 16:44:42 +02:00
2012-12-01 21:15:40 +01:00
pair - > diff . period_ratio_delta = new_percent - old_percent ;
pair - > diff . computed = true ;
return pair - > diff . period_ratio_delta ;
2012-10-05 16:44:42 +02:00
}
2012-10-21 23:31:51 +02:00
static double compute_ratio ( struct hist_entry * he , struct hist_entry * pair )
2012-10-05 16:44:42 +02:00
{
2012-12-01 21:15:40 +01:00
double old_period = he - > stat . period ? : 1 ;
double new_period = pair - > stat . period ;
2012-10-05 16:44:42 +02:00
2012-12-01 21:15:40 +01:00
pair - > diff . computed = true ;
pair - > diff . period_ratio = new_period / old_period ;
return pair - > diff . period_ratio ;
2012-10-05 16:44:42 +02:00
}
2012-10-21 23:31:51 +02:00
static s64 compute_wdiff ( struct hist_entry * he , struct hist_entry * pair )
2012-10-05 16:44:43 +02:00
{
2012-12-01 21:15:40 +01:00
u64 old_period = he - > stat . period ;
u64 new_period = pair - > stat . period ;
2012-10-05 16:44:43 +02:00
2012-12-01 21:15:40 +01:00
pair - > diff . computed = true ;
pair - > diff . wdiff = new_period * compute_wdiff_w2 -
old_period * compute_wdiff_w1 ;
2012-10-05 16:44:43 +02:00
2012-12-01 21:15:40 +01:00
return pair - > diff . wdiff ;
2012-10-05 16:44:43 +02:00
}
2012-11-28 14:52:41 +01:00
static int formula_delta ( struct hist_entry * he , struct hist_entry * pair ,
char * buf , size_t size )
2012-10-05 16:44:45 +02:00
{
2014-02-07 12:06:07 +09:00
u64 he_total = he - > hists - > stats . total_period ;
u64 pair_total = pair - > hists - > stats . total_period ;
if ( symbol_conf . filter_relative ) {
he_total = he - > hists - > stats . total_non_filtered_period ;
pair_total = pair - > hists - > stats . total_non_filtered_period ;
}
2012-10-05 16:44:45 +02:00
return scnprintf ( buf , size ,
" (% " PRIu64 " * 100 / % " PRIu64 " ) - "
" (% " PRIu64 " * 100 / % " PRIu64 " ) " ,
2014-02-07 12:06:07 +09:00
pair - > stat . period , pair_total ,
he - > stat . period , he_total ) ;
2012-10-05 16:44:45 +02:00
}
2012-11-28 14:52:41 +01:00
static int formula_ratio ( struct hist_entry * he , struct hist_entry * pair ,
char * buf , size_t size )
2012-10-05 16:44:45 +02:00
{
2012-12-01 21:15:40 +01:00
double old_period = he - > stat . period ;
double new_period = pair - > stat . period ;
2012-10-05 16:44:45 +02:00
return scnprintf ( buf , size , " %.0F / %.0F " , new_period , old_period ) ;
}
2012-11-28 14:52:41 +01:00
static int formula_wdiff ( struct hist_entry * he , struct hist_entry * pair ,
char * buf , size_t size )
2012-10-05 16:44:45 +02:00
{
2012-12-01 21:15:40 +01:00
u64 old_period = he - > stat . period ;
u64 new_period = pair - > stat . period ;
2012-10-05 16:44:45 +02:00
return scnprintf ( buf , size ,
" (% " PRIu64 " * " " % " PRId64 " ) - (% " PRIu64 " * " " % " PRId64 " ) " ,
new_period , compute_wdiff_w2 , old_period , compute_wdiff_w1 ) ;
}
2012-10-21 23:31:51 +02:00
static int formula_fprintf ( struct hist_entry * he , struct hist_entry * pair ,
char * buf , size_t size )
2012-10-05 16:44:45 +02:00
{
switch ( compute ) {
case COMPUTE_DELTA :
2012-11-28 14:52:41 +01:00
return formula_delta ( he , pair , buf , size ) ;
2012-10-05 16:44:45 +02:00
case COMPUTE_RATIO :
2012-11-28 14:52:41 +01:00
return formula_ratio ( he , pair , buf , size ) ;
2012-10-05 16:44:45 +02:00
case COMPUTE_WEIGHTED_DIFF :
2012-11-28 14:52:41 +01:00
return formula_wdiff ( he , pair , buf , size ) ;
2012-10-05 16:44:45 +02:00
default :
BUG_ON ( 1 ) ;
}
return - 1 ;
}
2013-10-22 19:01:31 -03:00
static int hists__add_entry ( struct hists * hists ,
2013-01-24 16:10:29 +01:00
struct addr_location * al , u64 period ,
2013-09-20 07:40:43 -07:00
u64 weight , u64 transaction )
2009-12-14 20:09:31 -02:00
{
2013-10-31 15:56:03 +09:00
if ( __hists__add_entry ( hists , al , NULL , NULL , NULL , period , weight ,
2012-09-11 13:34:27 +09:00
transaction , true ) ! = NULL )
2010-05-09 13:02:23 -03:00
return 0 ;
return - ENOMEM ;
2009-12-14 20:09:31 -02:00
}
2012-09-11 01:15:03 +03:00
static int diff__process_sample_event ( struct perf_tool * tool __maybe_unused ,
2011-11-25 08:19:45 -02:00
union perf_event * event ,
2011-01-29 13:02:00 -02:00
struct perf_sample * sample ,
2012-09-06 17:46:55 +02:00
struct perf_evsel * evsel ,
2011-11-28 07:56:39 -02:00
struct machine * machine )
2009-12-14 20:09:31 -02:00
{
struct addr_location al ;
2014-10-09 13:13:41 -03:00
struct hists * hists = evsel__hists ( evsel ) ;
2009-12-14 20:09:31 -02:00
2013-08-08 14:32:25 +03:00
if ( perf_event__preprocess_sample ( event , machine , & al , sample ) < 0 ) {
2009-12-14 20:09:31 -02:00
pr_warning ( " problem processing %d event, skipping it. \n " ,
event - > header . type ) ;
return - 1 ;
}
2014-10-09 13:13:41 -03:00
if ( hists__add_entry ( hists , & al , sample - > period ,
2013-09-20 07:40:43 -07:00
sample - > weight , sample - > transaction ) ) {
2010-05-14 14:19:35 -03:00
pr_warning ( " problem incrementing symbol period, skipping event \n " ) ;
2009-12-14 20:09:31 -02:00
return - 1 ;
}
2014-04-22 11:44:21 +09:00
/*
* The total_period is updated here before going to the output
* tree since normally only the baseline hists will call
* hists__output_resort ( ) and precompute needs the total
* period in order to sort entries by percentage delta .
*/
2014-10-09 13:13:41 -03:00
hists - > stats . total_period + = sample - > period ;
2014-04-22 11:44:21 +09:00
if ( ! al . filtered )
2014-10-09 13:13:41 -03:00
hists - > stats . total_non_filtered_period + = sample - > period ;
2014-04-22 11:44:21 +09:00
2009-12-14 20:09:31 -02:00
return 0 ;
}
2012-09-06 17:46:55 +02:00
static struct perf_tool tool = {
. sample = diff__process_sample_event ,
. mmap = perf_event__process_mmap ,
2014-11-18 11:38:18 -05:00
. mmap2 = perf_event__process_mmap2 ,
2012-09-06 17:46:55 +02:00
. comm = perf_event__process_comm ,
2012-10-06 15:44:59 -03:00
. exit = perf_event__process_exit ,
. fork = perf_event__process_fork ,
2012-09-06 17:46:55 +02:00
. lost = perf_event__process_lost ,
2014-07-06 14:18:21 +02:00
. ordered_events = true ,
2012-09-06 17:46:55 +02:00
. ordering_requires_timestamps = true ,
2009-12-14 20:09:31 -02:00
} ;
2012-09-06 17:46:55 +02:00
static struct perf_evsel * evsel_match ( struct perf_evsel * evsel ,
struct perf_evlist * evlist )
{
struct perf_evsel * e ;
2014-01-10 10:37:27 -03:00
evlist__for_each ( evlist , e ) {
2012-09-06 17:46:55 +02:00
if ( perf_evsel__match2 ( evsel , e ) )
return e ;
2014-01-10 10:37:27 -03:00
}
2012-09-06 17:46:55 +02:00
return NULL ;
}
2012-12-10 17:29:55 +09:00
static void perf_evlist__collapse_resort ( struct perf_evlist * evlist )
2012-10-04 21:49:36 +09:00
{
struct perf_evsel * evsel ;
2014-01-10 10:37:27 -03:00
evlist__for_each ( evlist , evsel ) {
2014-10-09 13:13:41 -03:00
struct hists * hists = evsel__hists ( evsel ) ;
2012-10-04 21:49:36 +09:00
2013-10-11 14:15:38 +09:00
hists__collapse_resort ( hists , NULL ) ;
2012-10-04 21:49:36 +09:00
}
}
2015-01-08 09:45:45 +09:00
static struct data__file * fmt_to_data_file ( struct perf_hpp_fmt * fmt )
{
struct diff_hpp_fmt * dfmt = container_of ( fmt , struct diff_hpp_fmt , fmt ) ;
void * ptr = dfmt - dfmt - > idx ;
struct data__file * d = container_of ( ptr , struct data__file , fmt ) ;
return d ;
}
2012-11-25 23:10:20 +01:00
static struct hist_entry *
get_pair_data ( struct hist_entry * he , struct data__file * d )
{
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 ;
}
static struct hist_entry *
get_pair_fmt ( struct hist_entry * he , struct diff_hpp_fmt * dfmt )
{
2015-01-08 09:45:45 +09:00
struct data__file * d = fmt_to_data_file ( & dfmt - > fmt ) ;
2012-11-25 23:10:20 +01:00
return get_pair_data ( he , d ) ;
}
2012-10-05 16:44:40 +02:00
static void hists__baseline_only ( struct hists * hists )
{
2012-12-10 17:29:55 +09: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 16:44:40 +02:00
2012-12-10 17:29:55 +09:00
next = rb_first ( root ) ;
2012-10-05 16:44:40 +02:00
while ( next ! = NULL ) {
2012-12-10 17:29:55 +09:00
struct hist_entry * he = rb_entry ( next , struct hist_entry , rb_node_in ) ;
2012-10-05 16:44:40 +02:00
2012-12-10 17:29:55 +09:00
next = rb_next ( & he - > rb_node_in ) ;
2012-10-25 14:42:45 -02:00
if ( ! hist_entry__next_pair ( he ) ) {
2012-12-10 17:29:55 +09:00
rb_erase ( & he - > rb_node_in , root ) ;
2014-12-19 12:31:40 -03:00
hist_entry__delete ( he ) ;
2012-10-05 16:44:40 +02:00
}
}
}
2012-10-05 16:44:42 +02:00
static void hists__precompute ( struct hists * hists )
{
2012-12-13 14:08:59 +01: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 16:44:42 +02:00
2012-12-13 14:08:59 +01:00
next = rb_first ( root ) ;
2012-10-05 16:44:42 +02:00
while ( next ! = NULL ) {
2012-11-25 23:10:20 +01:00
struct hist_entry * he , * pair ;
2015-01-08 09:45:47 +09:00
struct data__file * d ;
int i ;
2012-10-05 16:44:42 +02:00
2012-11-25 23:10:20 +01:00
he = rb_entry ( next , struct hist_entry , rb_node_in ) ;
2012-12-13 14:08:59 +01:00
next = rb_next ( & he - > rb_node_in ) ;
2012-11-25 23:10:20 +01:00
2015-01-08 09:45:47 +09:00
data__for_each_file_new ( i , d ) {
pair = get_pair_data ( he , d ) ;
if ( ! pair )
continue ;
switch ( compute ) {
case COMPUTE_DELTA :
compute_delta ( he , pair ) ;
break ;
case COMPUTE_RATIO :
compute_ratio ( he , pair ) ;
break ;
case COMPUTE_WEIGHTED_DIFF :
compute_wdiff ( he , pair ) ;
break ;
default :
BUG_ON ( 1 ) ;
}
2012-10-05 16:44:42 +02:00
}
}
}
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
2012-11-25 23:10:20 +01:00
__hist_entry__cmp_compute ( struct hist_entry * left , struct hist_entry * right ,
2012-10-05 16:44:42 +02:00
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 16:44:43 +02:00
case COMPUTE_WEIGHTED_DIFF :
{
s64 l = left - > diff . wdiff ;
s64 r = right - > diff . wdiff ;
return r - l ;
}
2012-10-05 16:44:42 +02:00
default :
BUG_ON ( 1 ) ;
}
return 0 ;
}
2012-11-25 23:10:20 +01:00
static int64_t
hist_entry__cmp_compute ( struct hist_entry * left , struct hist_entry * right ,
2015-01-08 09:45:47 +09:00
int c , int sort_idx )
2012-11-25 23:10:20 +01:00
{
bool pairs_left = hist_entry__has_pairs ( left ) ;
bool pairs_right = hist_entry__has_pairs ( right ) ;
struct hist_entry * p_right , * p_left ;
if ( ! pairs_left & & ! pairs_right )
return 0 ;
if ( ! pairs_left | | ! pairs_right )
return pairs_left ? - 1 : 1 ;
2015-01-08 09:45:47 +09:00
p_left = get_pair_data ( left , & data__files [ sort_idx ] ) ;
p_right = get_pair_data ( right , & data__files [ sort_idx ] ) ;
2012-11-25 23:10:20 +01:00
if ( ! p_left & & ! p_right )
return 0 ;
if ( ! p_left | | ! p_right )
return p_left ? - 1 : 1 ;
/*
* We have 2 entries of same kind , let ' s
* make the data comparison .
*/
return __hist_entry__cmp_compute ( p_left , p_right , c ) ;
}
2015-01-08 09:45:48 +09:00
static int64_t
hist_entry__cmp_compute_idx ( struct hist_entry * left , struct hist_entry * right ,
int c , int sort_idx )
{
struct hist_entry * p_right , * p_left ;
p_left = get_pair_data ( left , & data__files [ sort_idx ] ) ;
p_right = get_pair_data ( right , & data__files [ sort_idx ] ) ;
if ( ! p_left & & ! p_right )
return 0 ;
if ( ! p_left | | ! p_right )
return p_left ? - 1 : 1 ;
if ( c ! = COMPUTE_DELTA ) {
/*
* The delta can be computed without the baseline , but
* others are not . Put those entries which have no
* values below .
*/
if ( left - > dummy & & right - > dummy )
return 0 ;
if ( left - > dummy | | right - > dummy )
return left - > dummy ? 1 : - 1 ;
}
return __hist_entry__cmp_compute ( p_left , p_right , c ) ;
}
2014-12-27 14:06:29 +09:00
static int64_t
2015-01-08 09:45:46 +09:00
hist_entry__cmp_nop ( struct perf_hpp_fmt * fmt __maybe_unused ,
struct hist_entry * left __maybe_unused ,
2014-12-27 14:06:29 +09:00
struct hist_entry * right __maybe_unused )
{
return 0 ;
}
static int64_t
2015-01-08 09:45:46 +09:00
hist_entry__cmp_baseline ( struct perf_hpp_fmt * fmt __maybe_unused ,
struct hist_entry * left , struct hist_entry * right )
2014-12-27 14:06:29 +09:00
{
if ( left - > stat . period = = right - > stat . period )
return 0 ;
return left - > stat . period > right - > stat . period ? 1 : - 1 ;
}
static int64_t
2015-01-08 09:45:47 +09:00
hist_entry__cmp_delta ( struct perf_hpp_fmt * fmt ,
2015-01-08 09:45:46 +09:00
struct hist_entry * left , struct hist_entry * right )
2014-12-27 14:06:29 +09:00
{
2015-01-08 09:45:47 +09:00
struct data__file * d = fmt_to_data_file ( fmt ) ;
return hist_entry__cmp_compute ( right , left , COMPUTE_DELTA , d - > idx ) ;
2014-12-27 14:06:29 +09:00
}
static int64_t
2015-01-08 09:45:47 +09:00
hist_entry__cmp_ratio ( struct perf_hpp_fmt * fmt ,
2015-01-08 09:45:46 +09:00
struct hist_entry * left , struct hist_entry * right )
2014-12-27 14:06:29 +09:00
{
2015-01-08 09:45:47 +09:00
struct data__file * d = fmt_to_data_file ( fmt ) ;
return hist_entry__cmp_compute ( right , left , COMPUTE_RATIO , d - > idx ) ;
2014-12-27 14:06:29 +09:00
}
static int64_t
2015-01-08 09:45:47 +09:00
hist_entry__cmp_wdiff ( struct perf_hpp_fmt * fmt ,
2015-01-08 09:45:46 +09:00
struct hist_entry * left , struct hist_entry * right )
2014-12-27 14:06:29 +09:00
{
2015-01-08 09:45:47 +09:00
struct data__file * d = fmt_to_data_file ( fmt ) ;
return hist_entry__cmp_compute ( right , left , COMPUTE_WEIGHTED_DIFF , d - > idx ) ;
2014-12-27 14:06:29 +09:00
}
2015-01-08 09:45:48 +09:00
static int64_t
hist_entry__cmp_delta_idx ( struct perf_hpp_fmt * fmt __maybe_unused ,
struct hist_entry * left , struct hist_entry * right )
{
return hist_entry__cmp_compute_idx ( right , left , COMPUTE_DELTA ,
sort_compute ) ;
}
static int64_t
hist_entry__cmp_ratio_idx ( struct perf_hpp_fmt * fmt __maybe_unused ,
struct hist_entry * left , struct hist_entry * right )
{
return hist_entry__cmp_compute_idx ( right , left , COMPUTE_RATIO ,
sort_compute ) ;
}
static int64_t
hist_entry__cmp_wdiff_idx ( struct perf_hpp_fmt * fmt __maybe_unused ,
struct hist_entry * left , struct hist_entry * right )
{
return hist_entry__cmp_compute_idx ( right , left , COMPUTE_WEIGHTED_DIFF ,
sort_compute ) ;
}
2012-12-01 22:00:00 +01:00
static void hists__process ( struct hists * hists )
2012-10-05 16:44:40 +02:00
{
if ( show_baseline_only )
2012-12-01 22:00:00 +01:00
hists__baseline_only ( hists ) ;
2012-10-05 16:44:40 +02:00
2015-01-08 09:45:47 +09:00
hists__precompute ( hists ) ;
2014-12-27 14:06:30 +09:00
hists__output_resort ( hists , NULL ) ;
2012-10-05 16:44:42 +02:00
2012-12-01 22:00:00 +01:00
hists__fprintf ( hists , true , 0 , 0 , 0 , stdout ) ;
2012-10-05 16:44:40 +02:00
}
2012-12-01 21:56:03 +01: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 " ,
2013-10-15 16:27:32 +02:00
d - > idx , d - > file . path ,
2012-12-01 21:56:03 +01:00
! d - > idx ? " (Baseline) " : " " ) ;
fprintf ( stdout , " # \n " ) ;
}
2013-03-25 00:02:01 +01:00
static void data_process ( void )
2009-12-14 20:09:31 -02:00
{
2012-12-01 22:00:00 +01:00
struct perf_evlist * evlist_base = data__files [ 0 ] . session - > evlist ;
struct perf_evsel * evsel_base ;
2012-09-06 17:46:55 +02:00
bool first = true ;
2009-12-14 20:09:31 -02:00
2014-01-10 10:37:27 -03:00
evlist__for_each ( evlist_base , evsel_base ) {
2014-10-09 13:13:41 -03:00
struct hists * hists_base = evsel__hists ( evsel_base ) ;
2012-12-01 22:00:00 +01:00
struct data__file * d ;
int i ;
2009-12-14 20:09:31 -02:00
2012-12-01 22:00:00 +01:00
data__for_each_file_new ( i , d ) {
struct perf_evlist * evlist = d - > session - > evlist ;
struct perf_evsel * evsel ;
2014-10-09 13:13:41 -03:00
struct hists * hists ;
2012-12-01 22:00:00 +01:00
evsel = evsel_match ( evsel_base , evlist ) ;
if ( ! evsel )
continue ;
2014-10-09 13:13:41 -03:00
hists = evsel__hists ( evsel ) ;
d - > hists = hists ;
2012-12-01 22:00:00 +01:00
2014-10-09 13:13:41 -03:00
hists__match ( hists_base , hists ) ;
2012-12-01 22:00:00 +01:00
if ( ! show_baseline_only )
2014-10-09 13:13:41 -03:00
hists__link ( hists_base , hists ) ;
2012-12-01 22:00:00 +01:00
}
2009-12-14 20:09:31 -02:00
2013-03-25 00:02:01 +01:00
fprintf ( stdout , " %s# Event '%s' \n # \n " , first ? " " : " \n " ,
2012-12-01 22:00:00 +01:00
perf_evsel__name ( evsel_base ) ) ;
2012-09-06 17:46:55 +02:00
2013-03-25 00:02:01 +01:00
first = false ;
2012-09-06 17:46:55 +02:00
2012-12-01 22:00:00 +01:00
if ( verbose | | data__files_cnt > 2 )
2012-12-01 21:56:03 +01:00
data__fprintf ( ) ;
2014-10-09 13:13:41 -03:00
hists__process ( hists_base ) ;
2013-03-25 00:02:01 +01:00
}
}
2012-09-06 17:46:55 +02:00
2012-12-01 21:57:04 +01: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 ] ;
2013-12-27 16:55:14 -03:00
zfree ( & fmt - > header ) ;
2012-12-01 21:57:04 +01:00
}
}
2013-03-25 00:02:01 +01:00
static int __cmd_diff ( void )
{
struct data__file * d ;
int ret = - EINVAL , i ;
data__for_each_file ( i , d ) {
2013-10-15 16:27:32 +02:00
d - > session = perf_session__new ( & d - > file , false , & tool ) ;
2013-03-25 00:02:01 +01:00
if ( ! d - > session ) {
2013-10-15 16:27:32 +02:00
pr_err ( " Failed to open %s \n " , d - > file . path ) ;
2014-09-24 10:33:37 +09:00
ret = - 1 ;
2013-03-25 00:02:01 +01:00
goto out_delete ;
}
2012-09-06 17:46:55 +02:00
2015-03-03 11:58:45 -03:00
ret = perf_session__process_events ( d - > session ) ;
2013-03-25 00:02:01 +01:00
if ( ret ) {
2013-10-15 16:27:32 +02:00
pr_err ( " Failed to process %s \n " , d - > file . path ) ;
2013-03-25 00:02:01 +01:00
goto out_delete ;
}
2012-09-06 17:46:55 +02:00
2013-03-25 00:02:01 +01:00
perf_evlist__collapse_resort ( d - > session - > evlist ) ;
}
data_process ( ) ;
2012-09-06 17:46:55 +02:00
2013-03-25 00:02:01 +01:00
out_delete :
data__for_each_file ( i , d ) {
if ( d - > session )
perf_session__delete ( d - > session ) ;
2012-12-01 21:57:04 +01:00
data__free ( d ) ;
2012-09-06 17:46:55 +02:00
}
2009-12-28 22:48:36 -02:00
2013-03-25 00:02:01 +01:00
free ( data__files ) ;
2009-12-14 20:09:31 -02:00
return ret ;
}
2009-12-18 16:35:58 -02:00
static const char * const diff_usage [ ] = {
2009-12-14 20:09:31 -02:00
" perf diff [<options>] [old_file] [new_file] " ,
2009-12-18 16:35:58 -02:00
NULL ,
2009-12-14 20:09:31 -02:00
} ;
static const struct option options [ ] = {
2010-04-13 18:37:33 +10:00
OPT_INCR ( ' v ' , " verbose " , & verbose ,
2009-12-14 20:09:31 -02:00
" be more verbose (show symbol address, etc) " ) ,
2012-10-05 16:44:40 +02:00
OPT_BOOLEAN ( ' b ' , " baseline-only " , & show_baseline_only ,
" Show only items with match in baseline " ) ,
2012-10-05 16:44:43 +02:00
OPT_CALLBACK ( ' c ' , " compute " , & compute ,
" delta,ratio,wdiff:w1,w2 (default delta) " ,
2012-10-05 16:44:41 +02:00
" Entries differential computation selection " ,
setup_compute ) ,
2012-10-05 16:44:44 +02:00
OPT_BOOLEAN ( ' p ' , " period " , & show_period ,
" Show period values. " ) ,
2012-10-05 16:44:45 +02:00
OPT_BOOLEAN ( ' F ' , " formula " , & show_formula ,
" Show formula. " ) ,
2009-12-14 20:09:31 -02: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-15 20:04:41 -02: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 13:49:27 -02:00
OPT_STRING ( ' s ' , " sort " , & sort_order , " key[,key2...] " ,
2014-03-04 09:06:42 +09:00
" sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ... "
" Please refer the man page for the complete list. " ) ,
2015-03-20 02:57:52 +00:00
OPT_STRING_NOEMPTY ( ' t ' , " field-separator " , & symbol_conf . field_sep , " separator " ,
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 13:49:27 -02:00
" separator for columns, no spaces will be added between "
" columns '.' is reserved. " ) ,
2010-12-09 13:27:07 -07:00
OPT_STRING ( 0 , " symfs " , & symbol_conf . symfs , " directory " ,
" Look for files with symbols relative to this directory " ) ,
2012-11-25 23:10:20 +01:00
OPT_UINTEGER ( ' o ' , " order " , & sort_compute , " Specify compute sorting. " ) ,
2014-02-07 12:06:07 +09:00
OPT_CALLBACK ( 0 , " percentage " , NULL , " relative|absolute " ,
" How to display percentage of filtered entries " , parse_filter_percentage ) ,
2009-12-14 20:09:31 -02:00
OPT_END ( )
} ;
2013-02-03 20:08:34 +01:00
static double baseline_percent ( struct hist_entry * he )
2012-10-04 21:49:39 +09:00
{
2014-02-07 12:06:07 +09:00
u64 total = hists__total_period ( he - > hists ) ;
return 100.0 * he - > stat . period / total ;
2013-02-03 20:08:34 +01:00
}
2012-10-05 16:44:41 +02:00
2013-02-03 20:08:34 +01: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 ;
}
2013-12-30 13:04:19 +05:30
static int __hpp__color_compare ( struct perf_hpp_fmt * fmt ,
struct perf_hpp * hpp , struct hist_entry * he ,
int comparison_method )
{
struct diff_hpp_fmt * dfmt =
container_of ( fmt , struct diff_hpp_fmt , fmt ) ;
struct hist_entry * pair = get_pair_fmt ( he , dfmt ) ;
double diff ;
2013-12-30 13:32:35 +05:30
s64 wdiff ;
2013-12-30 13:04:19 +05:30
char pfmt [ 20 ] = " " ;
if ( ! pair )
2014-12-27 14:06:31 +09:00
goto no_print ;
2013-12-30 13:04:19 +05:30
switch ( comparison_method ) {
case COMPUTE_DELTA :
if ( pair - > diff . computed )
diff = pair - > diff . period_ratio_delta ;
else
diff = compute_delta ( he , pair ) ;
scnprintf ( pfmt , 20 , " %%%+d.2f%%%% " , dfmt - > header_width - 1 ) ;
return percent_color_snprintf ( hpp - > buf , hpp - > size ,
pfmt , diff ) ;
2013-12-30 13:04:20 +05:30
case COMPUTE_RATIO :
if ( he - > dummy )
goto dummy_print ;
if ( pair - > diff . computed )
diff = pair - > diff . period_ratio ;
else
diff = compute_ratio ( he , pair ) ;
scnprintf ( pfmt , 20 , " %%%d.6f " , dfmt - > header_width ) ;
return value_color_snprintf ( hpp - > buf , hpp - > size ,
pfmt , diff ) ;
2013-12-30 13:32:35 +05:30
case COMPUTE_WEIGHTED_DIFF :
if ( he - > dummy )
goto dummy_print ;
if ( pair - > diff . computed )
wdiff = pair - > diff . wdiff ;
else
wdiff = compute_wdiff ( he , pair ) ;
scnprintf ( pfmt , 20 , " %%14ld " , dfmt - > header_width ) ;
return color_snprintf ( hpp - > buf , hpp - > size ,
get_percent_color ( wdiff ) ,
pfmt , wdiff ) ;
2013-12-30 13:04:19 +05:30
default :
BUG_ON ( 1 ) ;
}
dummy_print :
2014-12-27 14:06:31 +09:00
return scnprintf ( hpp - > buf , hpp - > size , " %*s " ,
dfmt - > header_width , " N/A " ) ;
no_print :
2013-12-30 13:04:19 +05:30
return scnprintf ( hpp - > buf , hpp - > size , " %*s " ,
dfmt - > header_width , pfmt ) ;
}
static int hpp__color_delta ( struct perf_hpp_fmt * fmt ,
struct perf_hpp * hpp , struct hist_entry * he )
{
return __hpp__color_compare ( fmt , hpp , he , COMPUTE_DELTA ) ;
}
2013-12-30 13:04:20 +05:30
static int hpp__color_ratio ( struct perf_hpp_fmt * fmt ,
struct perf_hpp * hpp , struct hist_entry * he )
{
return __hpp__color_compare ( fmt , hpp , he , COMPUTE_RATIO ) ;
}
2013-12-30 13:32:35 +05:30
static int hpp__color_wdiff ( struct perf_hpp_fmt * fmt ,
struct perf_hpp * hpp , struct hist_entry * he )
{
return __hpp__color_compare ( fmt , hpp , he , COMPUTE_WEIGHTED_DIFF ) ;
}
2013-02-03 20:08:34 +01:00
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 16:44:41 +02:00
break ;
2013-02-03 20:08:34 +01:00
default :
2012-10-05 16:44:43 +02:00
break ;
2013-02-03 20:08:34 +01: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-21 23:31:51 +02:00
diff = compute_delta ( he , pair ) ;
2013-02-03 20:08:34 +01:00
2014-12-27 14:06:31 +09:00
scnprintf ( buf , size , " %+4.2F%% " , diff ) ;
2013-02-03 20:08:34 +01:00
break ;
case PERF_HPP_DIFF__RATIO :
/* No point for ratio number if we are dummy.. */
2014-12-27 14:06:31 +09:00
if ( he - > dummy ) {
scnprintf ( buf , size , " N/A " ) ;
2013-02-03 20:08:34 +01:00
break ;
2014-12-27 14:06:31 +09:00
}
2013-02-03 20:08:34 +01:00
if ( pair - > diff . computed )
ratio = pair - > diff . period_ratio ;
else
2012-10-21 23:31:51 +02:00
ratio = compute_ratio ( he , pair ) ;
2013-02-03 20:08:34 +01: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.. */
2014-12-27 14:06:31 +09:00
if ( he - > dummy ) {
scnprintf ( buf , size , " N/A " ) ;
2013-02-03 20:08:34 +01:00
break ;
2014-12-27 14:06:31 +09:00
}
2013-02-03 20:08:34 +01:00
if ( pair - > diff . computed )
wdiff = pair - > diff . wdiff ;
else
2012-10-21 23:31:51 +02:00
wdiff = compute_wdiff ( he , pair ) ;
2013-02-03 20:08:34 +01:00
if ( wdiff ! = 0 )
scnprintf ( buf , size , " %14ld " , wdiff ) ;
break ;
case PERF_HPP_DIFF__FORMULA :
2012-10-21 23:31:51 +02:00
formula_fprintf ( he , pair , buf , size ) ;
2012-10-05 16:44:41 +02:00
break ;
2013-02-03 20:08:34 +01:00
case PERF_HPP_DIFF__PERIOD :
scnprintf ( buf , size , " % " PRIu64 , pair - > stat . period ) ;
break ;
2012-10-05 16:44:41 +02:00
default :
BUG_ON ( 1 ) ;
} ;
2013-02-03 20:08:34 +01:00
}
static void
2012-12-01 22:00:00 +01:00
__hpp__entry_global ( struct hist_entry * he , struct diff_hpp_fmt * dfmt ,
char * buf , size_t size )
2013-02-03 20:08:34 +01:00
{
2012-11-25 23:10:20 +01:00
struct hist_entry * pair = get_pair_fmt ( he , dfmt ) ;
2012-12-01 22:00:00 +01:00
int idx = dfmt - > idx ;
2013-02-03 20:08:34 +01: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-01 22:00:00 +01:00
__hpp__entry_global ( he , dfmt , buf , MAX_COL_WIDTH ) ;
2013-02-03 20:08:34 +01: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 ) ;
}
2014-03-10 16:43:52 +09:00
static int hpp__header ( struct perf_hpp_fmt * fmt , struct perf_hpp * hpp ,
struct perf_evsel * evsel __maybe_unused )
2013-02-03 20:08:34 +01:00
{
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 ,
2014-03-10 16:43:52 +09:00
struct perf_hpp * hpp __maybe_unused ,
struct perf_evsel * evsel __maybe_unused )
2013-02-03 20:08:34 +01:00
{
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-01 22:00:00 +01:00
static void init_header ( struct data__file * d , struct diff_hpp_fmt * dfmt )
2013-02-03 20:08:34 +01: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-01 22:00:00 +01:00
if ( data__files_cnt > 2 )
scnprintf ( buf , MAX_HEADER_NAME , " %s/%d " , header , d - > idx ) ;
2013-02-03 20:08:34 +01: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-01 21:57:04 +01:00
static void data__hpp_register ( struct data__file * d , int idx )
2013-02-03 20:08:34 +01:00
{
2012-12-01 21:57:04 +01: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 ;
2014-12-27 14:06:29 +09:00
fmt - > cmp = hist_entry__cmp_nop ;
fmt - > collapse = hist_entry__cmp_nop ;
2012-12-01 21:57:04 +01:00
/* TODO more colors */
2013-12-30 13:04:19 +05:30
switch ( idx ) {
case PERF_HPP_DIFF__BASELINE :
2012-12-01 21:57:04 +01:00
fmt - > color = hpp__color_baseline ;
2014-12-27 14:06:29 +09:00
fmt - > sort = hist_entry__cmp_baseline ;
2013-12-30 13:04:19 +05:30
break ;
case PERF_HPP_DIFF__DELTA :
fmt - > color = hpp__color_delta ;
2014-12-27 14:06:29 +09:00
fmt - > sort = hist_entry__cmp_delta ;
2013-12-30 13:04:19 +05:30
break ;
2013-12-30 13:04:20 +05:30
case PERF_HPP_DIFF__RATIO :
fmt - > color = hpp__color_ratio ;
2014-12-27 14:06:29 +09:00
fmt - > sort = hist_entry__cmp_ratio ;
2013-12-30 13:04:20 +05:30
break ;
2013-12-30 13:32:35 +05:30
case PERF_HPP_DIFF__WEIGHTED_DIFF :
fmt - > color = hpp__color_wdiff ;
2014-12-27 14:06:29 +09:00
fmt - > sort = hist_entry__cmp_wdiff ;
2013-12-30 13:32:35 +05:30
break ;
2013-12-30 13:04:19 +05:30
default :
2014-12-27 14:06:29 +09:00
fmt - > sort = hist_entry__cmp_nop ;
2013-12-30 13:04:19 +05:30
break ;
}
2013-02-03 20:08:34 +01:00
2012-12-01 22:00:00 +01:00
init_header ( d , dfmt ) ;
2012-12-01 21:57:04 +01:00
perf_hpp__column_register ( fmt ) ;
2014-12-27 14:06:29 +09:00
perf_hpp__register_sort_field ( fmt ) ;
2013-02-03 20:08:34 +01:00
}
2015-01-08 09:45:48 +09:00
static int ui_init ( void )
2013-02-03 20:08:34 +01:00
{
2012-12-01 21:57:04 +01:00
struct data__file * d ;
2015-01-08 09:45:48 +09:00
struct perf_hpp_fmt * fmt ;
2012-12-01 21:57:04 +01:00
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 21:49:39 +09:00
2012-12-01 21:57:04 +01: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 16:44:45 +02:00
2012-12-01 21:57:04 +01:00
if ( show_period )
data__hpp_register ( d , i ? PERF_HPP_DIFF__PERIOD :
PERF_HPP_DIFF__PERIOD_BASELINE ) ;
2012-10-05 16:44:44 +02:00
}
2015-01-08 09:45:48 +09:00
if ( ! sort_compute )
return 0 ;
/*
* Prepend an fmt to sort on columns at ' sort_compute ' first .
* This fmt is added only to the sort list but not to the
* output fields list .
*
* Note that this column ( data ) can be compared twice - one
* for this ' sort_compute ' fmt and another for the normal
* diff_hpp_fmt . But it shouldn ' t a problem as most entries
* will be sorted out by first try or baseline and comparing
* is not a costly operation .
*/
fmt = zalloc ( sizeof ( * fmt ) ) ;
if ( fmt = = NULL ) {
pr_err ( " Memory allocation failed \n " ) ;
return - 1 ;
}
fmt - > cmp = hist_entry__cmp_nop ;
fmt - > collapse = hist_entry__cmp_nop ;
switch ( compute ) {
case COMPUTE_DELTA :
fmt - > sort = hist_entry__cmp_delta_idx ;
break ;
case COMPUTE_RATIO :
fmt - > sort = hist_entry__cmp_ratio_idx ;
break ;
case COMPUTE_WEIGHTED_DIFF :
fmt - > sort = hist_entry__cmp_wdiff_idx ;
break ;
default :
BUG_ON ( 1 ) ;
}
list_add ( & fmt - > sort_list , & perf_hpp__sort_list ) ;
return 0 ;
2012-10-04 21:49:39 +09:00
}
2013-03-25 00:02:01 +01:00
static int data_init ( int argc , const char * * argv )
2009-12-14 20:09:31 -02:00
{
2013-03-25 00:02:01 +01:00
struct data__file * d ;
static const char * defaults [ ] = {
" perf.data.old " ,
" perf.data " ,
} ;
2012-12-01 22:00:00 +01:00
bool use_default = true ;
2013-03-25 00:02:01 +01:00
int i ;
data__files_cnt = 2 ;
2009-12-14 20:09:31 -02:00
if ( argc ) {
2012-12-01 22:00:00 +01:00
if ( argc = = 1 )
2013-03-25 00:02:01 +01:00
defaults [ 1 ] = argv [ 0 ] ;
2012-12-01 22:00:00 +01:00
else {
data__files_cnt = argc ;
use_default = false ;
}
2013-12-06 17:25:52 -05:00
} else if ( perf_guest ) {
2013-03-25 00:02:01 +01:00
defaults [ 0 ] = " perf.data.host " ;
defaults [ 1 ] = " perf.data.guest " ;
2009-12-14 20:09:31 -02:00
}
2012-11-25 23:10:20 +01:00
if ( sort_compute > = ( unsigned int ) data__files_cnt ) {
pr_err ( " Order option out of limit. \n " ) ;
return - EINVAL ;
}
2013-03-25 00:02:01 +01:00
data__files = zalloc ( sizeof ( * data__files ) * data__files_cnt ) ;
if ( ! data__files )
return - ENOMEM ;
data__for_each_file ( i , d ) {
2013-10-15 16:27:32 +02:00
struct perf_data_file * file = & d - > file ;
file - > path = use_default ? defaults [ i ] : argv [ i ] ;
file - > mode = PERF_DATA_MODE_READ ,
file - > force = force ,
2013-03-25 00:02:01 +01:00
d - > idx = i ;
}
return 0 ;
}
int cmd_diff ( int argc , const char * * argv , const char * prefix __maybe_unused )
{
2014-10-22 15:02:41 -04:00
int ret = hists__init ( ) ;
if ( ret < 0 )
return ret ;
2014-01-14 12:02:15 +09:00
perf_config ( perf_default_config , NULL ) ;
2013-03-25 00:02:01 +01:00
argc = parse_options ( argc , argv , options , diff_usage , 0 ) ;
2014-08-12 15:40:45 +09:00
if ( symbol__init ( NULL ) < 0 )
2009-12-15 20:04:40 -02:00
return - 1 ;
2013-03-25 00:02:01 +01:00
if ( data_init ( argc , argv ) < 0 )
return - 1 ;
2015-01-08 09:45:48 +09:00
if ( ui_init ( ) < 0 )
return - 1 ;
2012-10-04 21:49:39 +09:00
2014-03-18 11:31:39 +09:00
sort__mode = SORT_MODE__DIFF ;
2013-02-06 14:57:16 +09:00
if ( setup_sorting ( ) < 0 )
usage_with_options ( diff_usage , options ) ;
2009-12-14 20:09:31 -02: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 13:49:27 -02:00
2013-04-03 21:26:19 +09: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 13:49:27 -02:00
2009-12-14 20:09:31 -02:00
return __cmd_diff ( ) ;
}