2012-12-10 12:29:57 +04:00
# include "perf.h"
# include "tests.h"
# include "debug.h"
# include "symbol.h"
# include "sort.h"
# include "evsel.h"
# include "evlist.h"
# include "machine.h"
# include "thread.h"
# include "parse-events.h"
2014-04-25 07:28:13 +04:00
# include "hists_common.h"
2012-12-10 12:29:57 +04:00
struct sample {
u32 pid ;
u64 ip ;
struct thread * thread ;
struct map * map ;
struct symbol * sym ;
} ;
2014-04-25 07:28:13 +04:00
/* For the numbers, see hists_common.c */
2012-12-10 12:29:57 +04:00
static struct sample fake_common_samples [ ] = {
/* perf [kernel] schedule() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_PERF1 , . ip = FAKE_IP_KERNEL_SCHEDULE , } ,
2012-12-10 12:29:57 +04:00
/* perf [perf] main() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_PERF2 , . ip = FAKE_IP_PERF_MAIN , } ,
2012-12-10 12:29:57 +04:00
/* perf [perf] cmd_record() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_PERF2 , . ip = FAKE_IP_PERF_CMD_RECORD , } ,
2012-12-10 12:29:57 +04:00
/* bash [bash] xmalloc() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_BASH , . ip = FAKE_IP_BASH_XMALLOC , } ,
2012-12-10 12:29:57 +04:00
/* bash [libc] malloc() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_BASH , . ip = FAKE_IP_LIBC_MALLOC , } ,
2012-12-10 12:29:57 +04:00
} ;
static struct sample fake_samples [ ] [ 5 ] = {
{
/* perf [perf] run_command() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_PERF1 , . ip = FAKE_IP_PERF_RUN_COMMAND , } ,
2012-12-10 12:29:57 +04:00
/* perf [libc] malloc() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_PERF1 , . ip = FAKE_IP_LIBC_MALLOC , } ,
2012-12-10 12:29:57 +04:00
/* perf [kernel] page_fault() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_PERF1 , . ip = FAKE_IP_KERNEL_PAGE_FAULT , } ,
2012-12-10 12:29:57 +04:00
/* perf [kernel] sys_perf_event_open() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_PERF2 , . ip = FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN , } ,
2012-12-10 12:29:57 +04:00
/* bash [libc] free() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_BASH , . ip = FAKE_IP_LIBC_FREE , } ,
2012-12-10 12:29:57 +04:00
} ,
{
/* perf [libc] free() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_PERF2 , . ip = FAKE_IP_LIBC_FREE , } ,
2012-12-10 12:29:57 +04:00
/* bash [libc] malloc() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_BASH , . ip = FAKE_IP_LIBC_MALLOC , } , /* will be merged */
2012-12-10 12:29:57 +04:00
/* bash [bash] xfee() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_BASH , . ip = FAKE_IP_BASH_XFREE , } ,
2012-12-10 12:29:57 +04:00
/* bash [libc] realloc() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_BASH , . ip = FAKE_IP_LIBC_REALLOC , } ,
2012-12-10 12:29:57 +04:00
/* bash [kernel] page_fault() */
2014-05-23 09:59:57 +04:00
{ . pid = FAKE_PID_BASH , . ip = FAKE_IP_KERNEL_PAGE_FAULT , } ,
2012-12-10 12:29:57 +04:00
} ,
} ;
static int add_hist_entries ( struct perf_evlist * evlist , struct machine * machine )
{
struct perf_evsel * evsel ;
struct addr_location al ;
struct hist_entry * he ;
2014-05-23 09:59:57 +04:00
struct perf_sample sample = { . period = 1 , } ;
2012-12-10 12:29:57 +04:00
size_t i = 0 , k ;
/*
* each evsel will have 10 samples - 5 common and 5 distinct .
* However the second evsel also has a collapsed entry for
* " bash [libc] malloc " so total 9 entries will be in the tree .
*/
2014-01-10 17:37:27 +04:00
evlist__for_each ( evlist , evsel ) {
2012-12-10 12:29:57 +04:00
for ( k = 0 ; k < ARRAY_SIZE ( fake_common_samples ) ; k + + ) {
const union perf_event event = {
2013-08-27 12:23:06 +04:00
. header = {
. misc = PERF_RECORD_MISC_USER ,
2012-12-10 12:29:57 +04:00
} ,
} ;
2013-08-27 12:23:06 +04:00
sample . pid = fake_common_samples [ k ] . pid ;
2014-05-12 04:56:42 +04:00
sample . tid = fake_common_samples [ k ] . pid ;
2013-08-27 12:23:06 +04:00
sample . ip = fake_common_samples [ k ] . ip ;
2012-12-10 12:29:57 +04:00
if ( perf_event__preprocess_sample ( & event , machine , & al ,
2013-08-08 15:32:25 +04:00
& sample ) < 0 )
2012-12-10 12:29:57 +04:00
goto out ;
2013-09-20 18:40:43 +04:00
he = __hists__add_entry ( & evsel - > hists , & al , NULL ,
2012-09-11 08:34:27 +04:00
NULL , NULL , 1 , 1 , 0 , true ) ;
2012-12-10 12:29:57 +04:00
if ( he = = NULL )
goto out ;
fake_common_samples [ k ] . thread = al . thread ;
fake_common_samples [ k ] . map = al . map ;
fake_common_samples [ k ] . sym = al . sym ;
}
for ( k = 0 ; k < ARRAY_SIZE ( fake_samples [ i ] ) ; k + + ) {
const union perf_event event = {
2013-08-27 12:23:06 +04:00
. header = {
. misc = PERF_RECORD_MISC_USER ,
2012-12-10 12:29:57 +04:00
} ,
} ;
2013-08-27 12:23:06 +04:00
sample . pid = fake_samples [ i ] [ k ] . pid ;
2014-05-12 04:56:42 +04:00
sample . tid = fake_samples [ i ] [ k ] . pid ;
2013-08-27 12:23:06 +04:00
sample . ip = fake_samples [ i ] [ k ] . ip ;
2012-12-10 12:29:57 +04:00
if ( perf_event__preprocess_sample ( & event , machine , & al ,
2013-08-08 15:32:25 +04:00
& sample ) < 0 )
2012-12-10 12:29:57 +04:00
goto out ;
2013-10-31 10:56:03 +04:00
he = __hists__add_entry ( & evsel - > hists , & al , NULL ,
2012-09-11 08:34:27 +04:00
NULL , NULL , 1 , 1 , 0 , true ) ;
2012-12-10 12:29:57 +04:00
if ( he = = NULL )
goto out ;
fake_samples [ i ] [ k ] . thread = al . thread ;
fake_samples [ i ] [ k ] . map = al . map ;
fake_samples [ i ] [ k ] . sym = al . sym ;
}
i + + ;
}
return 0 ;
out :
pr_debug ( " Not enough memory for adding a hist entry \n " ) ;
return - 1 ;
}
static int find_sample ( struct sample * samples , size_t nr_samples ,
struct thread * t , struct map * m , struct symbol * s )
{
while ( nr_samples - - ) {
if ( samples - > thread = = t & & samples - > map = = m & &
samples - > sym = = s )
return 1 ;
samples + + ;
}
return 0 ;
}
static int __validate_match ( struct hists * hists )
{
size_t count = 0 ;
struct rb_root * root ;
struct rb_node * node ;
/*
* Only entries from fake_common_samples should have a pair .
*/
if ( sort__need_collapse )
root = & hists - > entries_collapsed ;
else
root = hists - > entries_in ;
node = rb_first ( root ) ;
while ( node ) {
struct hist_entry * he ;
he = rb_entry ( node , struct hist_entry , rb_node_in ) ;
if ( hist_entry__has_pairs ( he ) ) {
if ( find_sample ( fake_common_samples ,
ARRAY_SIZE ( fake_common_samples ) ,
he - > thread , he - > ms . map , he - > ms . sym ) ) {
count + + ;
} else {
pr_debug ( " Can't find the matched entry \n " ) ;
return - 1 ;
}
}
node = rb_next ( node ) ;
}
if ( count ! = ARRAY_SIZE ( fake_common_samples ) ) {
pr_debug ( " Invalid count for matched entries: %zd of %zd \n " ,
count , ARRAY_SIZE ( fake_common_samples ) ) ;
return - 1 ;
}
return 0 ;
}
static int validate_match ( struct hists * leader , struct hists * other )
{
return __validate_match ( leader ) | | __validate_match ( other ) ;
}
static int __validate_link ( struct hists * hists , int idx )
{
size_t count = 0 ;
size_t count_pair = 0 ;
size_t count_dummy = 0 ;
struct rb_root * root ;
struct rb_node * node ;
/*
* Leader hists ( idx = 0 ) will have dummy entries from other ,
* and some entries will have no pair . However every entry
* in other hists should have ( dummy ) pair .
*/
if ( sort__need_collapse )
root = & hists - > entries_collapsed ;
else
root = hists - > entries_in ;
node = rb_first ( root ) ;
while ( node ) {
struct hist_entry * he ;
he = rb_entry ( node , struct hist_entry , rb_node_in ) ;
if ( hist_entry__has_pairs ( he ) ) {
if ( ! find_sample ( fake_common_samples ,
ARRAY_SIZE ( fake_common_samples ) ,
he - > thread , he - > ms . map , he - > ms . sym ) & &
! find_sample ( fake_samples [ idx ] ,
ARRAY_SIZE ( fake_samples [ idx ] ) ,
he - > thread , he - > ms . map , he - > ms . sym ) ) {
count_dummy + + ;
}
count_pair + + ;
} else if ( idx ) {
pr_debug ( " A entry from the other hists should have pair \n " ) ;
return - 1 ;
}
count + + ;
node = rb_next ( node ) ;
}
/*
* Note that we have a entry collapsed in the other ( idx = 1 ) hists .
*/
if ( idx = = 0 ) {
if ( count_dummy ! = ARRAY_SIZE ( fake_samples [ 1 ] ) - 1 ) {
pr_debug ( " Invalid count of dummy entries: %zd of %zd \n " ,
count_dummy , ARRAY_SIZE ( fake_samples [ 1 ] ) - 1 ) ;
return - 1 ;
}
if ( count ! = count_pair + ARRAY_SIZE ( fake_samples [ 0 ] ) ) {
pr_debug ( " Invalid count of total leader entries: %zd of %zd \n " ,
count , count_pair + ARRAY_SIZE ( fake_samples [ 0 ] ) ) ;
return - 1 ;
}
} else {
if ( count ! = count_pair ) {
pr_debug ( " Invalid count of total other entries: %zd of %zd \n " ,
count , count_pair ) ;
return - 1 ;
}
if ( count_dummy > 0 ) {
pr_debug ( " Other hists should not have dummy entries: %zd \n " ,
count_dummy ) ;
return - 1 ;
}
}
return 0 ;
}
static int validate_link ( struct hists * leader , struct hists * other )
{
return __validate_link ( leader , 0 ) | | __validate_link ( other , 1 ) ;
}
int test__hists_link ( void )
{
int err = - 1 ;
2012-12-19 02:15:48 +04:00
struct machines machines ;
2012-12-10 12:29:57 +04:00
struct machine * machine = NULL ;
struct perf_evsel * evsel , * first ;
2013-03-11 11:43:12 +04:00
struct perf_evlist * evlist = perf_evlist__new ( ) ;
2012-12-10 12:29:57 +04:00
if ( evlist = = NULL )
return - ENOMEM ;
2013-01-15 17:39:51 +04:00
err = parse_events ( evlist , " cpu-clock " ) ;
2012-12-10 12:29:57 +04:00
if ( err )
goto out ;
2013-01-15 17:39:51 +04:00
err = parse_events ( evlist , " task-clock " ) ;
2012-12-10 12:29:57 +04:00
if ( err )
goto out ;
/* default sort order (comm,dso,sym) will be used */
2013-02-06 09:57:16 +04:00
if ( setup_sorting ( ) < 0 )
goto out ;
2012-12-10 12:29:57 +04:00
2012-12-19 02:15:48 +04:00
machines__init ( & machines ) ;
2012-12-10 12:29:57 +04:00
/* setup threads/dso/map/symbols also */
2012-12-19 02:15:48 +04:00
machine = setup_fake_machine ( & machines ) ;
2012-12-10 12:29:57 +04:00
if ( ! machine )
goto out ;
if ( verbose > 1 )
machine__fprintf ( machine , stderr ) ;
/* process sample events */
err = add_hist_entries ( evlist , machine ) ;
if ( err < 0 )
goto out ;
2014-01-10 17:37:27 +04:00
evlist__for_each ( evlist , evsel ) {
2013-10-11 09:15:38 +04:00
hists__collapse_resort ( & evsel - > hists , NULL ) ;
2012-12-10 12:29:57 +04:00
if ( verbose > 2 )
2014-05-12 05:06:18 +04:00
print_hists_in ( & evsel - > hists ) ;
2012-12-10 12:29:57 +04:00
}
first = perf_evlist__first ( evlist ) ;
evsel = perf_evlist__last ( evlist ) ;
/* match common entries */
hists__match ( & first - > hists , & evsel - > hists ) ;
err = validate_match ( & first - > hists , & evsel - > hists ) ;
if ( err )
goto out ;
/* link common and/or dummy entries */
hists__link ( & first - > hists , & evsel - > hists ) ;
err = validate_link ( & first - > hists , & evsel - > hists ) ;
if ( err )
goto out ;
err = 0 ;
out :
/* tear down everything */
perf_evlist__delete ( evlist ) ;
2014-05-12 09:43:18 +04:00
reset_output_field ( ) ;
2012-12-19 02:15:48 +04:00
machines__exit ( & machines ) ;
2012-12-10 12:29:57 +04:00
return err ;
}