2019-08-30 15:09:54 -03:00
# include "util/map_symbol.h"
2017-07-18 20:13:13 +08:00
# include "util/branch.h"
2019-08-29 16:18:59 -03:00
# include <linux/kernel.h>
2017-07-18 20:13:13 +08:00
static bool cross_area ( u64 addr1 , u64 addr2 , int size )
{
u64 align1 , align2 ;
align1 = addr1 & ~ ( size - 1 ) ;
align2 = addr2 & ~ ( size - 1 ) ;
return ( align1 ! = align2 ) ? true : false ;
}
# define AREA_4K 4096
# define AREA_2M (2 * 1024 * 1024)
void branch_type_count ( struct branch_type_stat * st , struct branch_flags * flags ,
u64 from , u64 to )
{
if ( flags - > type = = PERF_BR_UNKNOWN | | from = = 0 )
return ;
2022-08-24 10:18:20 +05:30
if ( flags - > type = = PERF_BR_EXTEND_ABI )
st - > new_counts [ flags - > new_type ] + + ;
else
st - > counts [ flags - > type ] + + ;
2017-07-18 20:13:13 +08:00
if ( flags - > type = = PERF_BR_COND ) {
if ( to > from )
st - > cond_fwd + + ;
else
st - > cond_bwd + + ;
}
if ( cross_area ( from , to , AREA_2M ) )
st - > cross_2m + + ;
else if ( cross_area ( from , to , AREA_4K ) )
st - > cross_4k + + ;
}
2022-08-24 10:18:20 +05:30
const char * branch_new_type_name ( int new_type )
{
const char * branch_new_names [ PERF_BR_NEW_MAX ] = {
" FAULT_ALGN " ,
" FAULT_DATA " ,
" FAULT_INST " ,
2022-08-24 10:18:22 +05:30
/*
* TODO : This switch should happen on ' session - > header . env . arch '
* instead , because an arm64 platform perf recording could be
* opened for analysis on other platforms as well .
*/
# ifdef __aarch64__
" ARM64_FIQ " ,
" ARM64_DEBUG_HALT " ,
" ARM64_DEBUG_EXIT " ,
" ARM64_DEBUG_INST " ,
" ARM64_DEBUG_DATA "
# else
2022-08-24 10:18:20 +05:30
" ARCH_1 " ,
" ARCH_2 " ,
" ARCH_3 " ,
" ARCH_4 " ,
" ARCH_5 "
2022-08-24 10:18:22 +05:30
# endif
2022-08-24 10:18:20 +05:30
} ;
if ( new_type > = 0 & & new_type < PERF_BR_NEW_MAX )
return branch_new_names [ new_type ] ;
return NULL ;
}
2017-07-18 20:13:13 +08:00
const char * branch_type_name ( int type )
{
const char * branch_names [ PERF_BR_MAX ] = {
" N/A " ,
" COND " ,
" UNCOND " ,
" IND " ,
" CALL " ,
" IND_CALL " ,
" RET " ,
" SYSCALL " ,
" SYSRET " ,
" COND_CALL " ,
2022-02-24 11:06:54 +05:30
" COND_RET " ,
" ERET " ,
2022-08-24 10:18:19 +05:30
" IRQ " ,
" SERROR " ,
2022-08-24 10:18:20 +05:30
" NO_TX " ,
" " , // Needed for PERF_BR_EXTEND_ABI that ends up triggering some compiler warnings about NULL deref
2017-07-18 20:13:13 +08:00
} ;
if ( type > = 0 & & type < PERF_BR_MAX )
return branch_names [ type ] ;
return NULL ;
}
2022-08-24 10:18:20 +05:30
const char * get_branch_type ( struct branch_entry * e )
{
if ( e - > flags . type = = PERF_BR_UNKNOWN )
return " " ;
if ( e - > flags . type = = PERF_BR_EXTEND_ABI )
return branch_new_type_name ( e - > flags . new_type ) ;
return branch_type_name ( e - > flags . type ) ;
}
2023-10-24 15:23:11 -07:00
void branch_type_stat_display ( FILE * fp , const struct branch_type_stat * st )
2017-07-18 20:13:13 +08:00
{
u64 total = 0 ;
int i ;
for ( i = 0 ; i < PERF_BR_MAX ; i + + )
total + = st - > counts [ i ] ;
if ( total = = 0 )
return ;
fprintf ( fp , " \n # " ) ;
fprintf ( fp , " \n # Branch Statistics: " ) ;
fprintf ( fp , " \n # " ) ;
if ( st - > cond_fwd > 0 ) {
fprintf ( fp , " \n %8s: %5.1f%% " ,
" COND_FWD " ,
100.0 * ( double ) st - > cond_fwd / ( double ) total ) ;
}
if ( st - > cond_bwd > 0 ) {
fprintf ( fp , " \n %8s: %5.1f%% " ,
" COND_BWD " ,
100.0 * ( double ) st - > cond_bwd / ( double ) total ) ;
}
if ( st - > cross_4k > 0 ) {
fprintf ( fp , " \n %8s: %5.1f%% " ,
" CROSS_4K " ,
100.0 * ( double ) st - > cross_4k / ( double ) total ) ;
}
if ( st - > cross_2m > 0 ) {
fprintf ( fp , " \n %8s: %5.1f%% " ,
" CROSS_2M " ,
100.0 * ( double ) st - > cross_2m / ( double ) total ) ;
}
for ( i = 0 ; i < PERF_BR_MAX ; i + + ) {
if ( st - > counts [ i ] > 0 )
fprintf ( fp , " \n %8s: %5.1f%% " ,
branch_type_name ( i ) ,
100.0 *
( double ) st - > counts [ i ] / ( double ) total ) ;
}
2022-08-24 10:18:20 +05:30
for ( i = 0 ; i < PERF_BR_NEW_MAX ; i + + ) {
if ( st - > new_counts [ i ] > 0 )
fprintf ( fp , " \n %8s: %5.1f%% " ,
branch_new_type_name ( i ) ,
100.0 *
( double ) st - > new_counts [ i ] / ( double ) total ) ;
}
2017-07-18 20:13:13 +08:00
}
static int count_str_scnprintf ( int idx , const char * str , char * bf , int size )
{
return scnprintf ( bf , size , " %s%s " , ( idx ) ? " " : " ( " , str ) ;
}
2023-10-24 15:23:11 -07:00
int branch_type_str ( const struct branch_type_stat * st , char * bf , int size )
2017-07-18 20:13:13 +08:00
{
int i , j = 0 , printed = 0 ;
u64 total = 0 ;
for ( i = 0 ; i < PERF_BR_MAX ; i + + )
total + = st - > counts [ i ] ;
2022-08-24 10:18:20 +05:30
for ( i = 0 ; i < PERF_BR_NEW_MAX ; i + + )
total + = st - > new_counts [ i ] ;
2017-07-18 20:13:13 +08:00
if ( total = = 0 )
return 0 ;
if ( st - > cond_fwd > 0 )
printed + = count_str_scnprintf ( j + + , " COND_FWD " , bf + printed , size - printed ) ;
if ( st - > cond_bwd > 0 )
printed + = count_str_scnprintf ( j + + , " COND_BWD " , bf + printed , size - printed ) ;
for ( i = 0 ; i < PERF_BR_MAX ; i + + ) {
if ( i = = PERF_BR_COND )
continue ;
if ( st - > counts [ i ] > 0 )
printed + = count_str_scnprintf ( j + + , branch_type_name ( i ) , bf + printed , size - printed ) ;
}
2022-08-24 10:18:20 +05:30
for ( i = 0 ; i < PERF_BR_NEW_MAX ; i + + ) {
if ( st - > new_counts [ i ] > 0 )
printed + = count_str_scnprintf ( j + + , branch_new_type_name ( i ) , bf + printed , size - printed ) ;
}
2017-07-18 20:13:13 +08:00
if ( st - > cross_4k > 0 )
printed + = count_str_scnprintf ( j + + , " CROSS_4K " , bf + printed , size - printed ) ;
if ( st - > cross_2m > 0 )
printed + = count_str_scnprintf ( j + + , " CROSS_2M " , bf + printed , size - printed ) ;
return printed ;
}
2023-02-02 17:56:14 +05:30
const char * branch_spec_desc ( int spec )
{
const char * branch_spec_outcomes [ PERF_BR_SPEC_MAX ] = {
" N/A " ,
" SPEC_WRONG_PATH " ,
" NON_SPEC_CORRECT_PATH " ,
" SPEC_CORRECT_PATH " ,
} ;
if ( spec > = 0 & & spec < PERF_BR_SPEC_MAX )
return branch_spec_outcomes [ spec ] ;
return NULL ;
}