2016-02-15 11:34:35 +03:00
# include <stddef.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
2016-02-24 11:46:42 +03:00
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <api/fs/fs.h>
2016-02-15 11:34:34 +03:00
# include "mem-events.h"
2016-02-15 11:34:35 +03:00
# include "debug.h"
2016-02-24 11:46:46 +03:00
# include "symbol.h"
2016-02-15 11:34:34 +03:00
2016-02-24 11:46:42 +03:00
# define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
2016-02-15 11:34:34 +03:00
struct perf_mem_event perf_mem_events [ PERF_MEM_EVENTS__MAX ] = {
2016-02-24 11:46:42 +03:00
E ( " ldlat-loads " , " cpu/mem-loads,ldlat=30/P " , " mem-loads " ) ,
E ( " ldlat-stores " , " cpu/mem-stores/P " , " mem-stores " ) ,
2016-02-15 11:34:34 +03:00
} ;
2016-02-24 11:46:42 +03:00
# undef E
2016-02-15 11:34:34 +03:00
# undef E
2016-02-15 11:34:35 +03:00
2016-02-24 11:46:43 +03:00
char * perf_mem_events__name ( int i )
{
return ( char * ) perf_mem_events [ i ] . name ;
}
2016-02-15 11:34:35 +03:00
int perf_mem_events__parse ( const char * str )
{
char * tok , * saveptr = NULL ;
bool found = false ;
char * buf ;
int j ;
/* We need buffer that we know we can write to. */
buf = malloc ( strlen ( str ) + 1 ) ;
if ( ! buf )
return - ENOMEM ;
strcpy ( buf , str ) ;
tok = strtok_r ( ( char * ) buf , " , " , & saveptr ) ;
while ( tok ) {
for ( j = 0 ; j < PERF_MEM_EVENTS__MAX ; j + + ) {
struct perf_mem_event * e = & perf_mem_events [ j ] ;
if ( strstr ( e - > tag , tok ) )
e - > record = found = true ;
}
tok = strtok_r ( NULL , " , " , & saveptr ) ;
}
free ( buf ) ;
if ( found )
return 0 ;
pr_err ( " failed: event '%s' not found, use '-e list' to get list of available events \n " , str ) ;
return - 1 ;
}
2016-02-24 11:46:42 +03:00
int perf_mem_events__init ( void )
{
const char * mnt = sysfs__mount ( ) ;
bool found = false ;
int j ;
if ( ! mnt )
return - ENOENT ;
for ( j = 0 ; j < PERF_MEM_EVENTS__MAX ; j + + ) {
char path [ PATH_MAX ] ;
struct perf_mem_event * e = & perf_mem_events [ j ] ;
struct stat st ;
scnprintf ( path , PATH_MAX , " %s/devices/cpu/events/%s " ,
mnt , e - > sysfs_name ) ;
if ( ! stat ( path , & st ) )
e - > supported = found = true ;
}
return found ? 0 : - ENOENT ;
}
2016-02-24 11:46:46 +03:00
static const char * const tlb_access [ ] = {
" N/A " ,
" HIT " ,
" MISS " ,
" L1 " ,
" L2 " ,
" Walker " ,
" Fault " ,
} ;
2016-02-24 11:46:50 +03:00
int perf_mem__tlb_scnprintf ( char * out , size_t sz , struct mem_info * mem_info )
2016-02-24 11:46:46 +03:00
{
size_t l = 0 , i ;
u64 m = PERF_MEM_TLB_NA ;
u64 hit , miss ;
sz - = 1 ; /* -1 for null termination */
out [ 0 ] = ' \0 ' ;
if ( mem_info )
m = mem_info - > data_src . mem_dtlb ;
hit = m & PERF_MEM_TLB_HIT ;
miss = m & PERF_MEM_TLB_MISS ;
/* already taken care of */
m & = ~ ( PERF_MEM_TLB_HIT | PERF_MEM_TLB_MISS ) ;
for ( i = 0 ; m & & i < ARRAY_SIZE ( tlb_access ) ; i + + , m > > = 1 ) {
if ( ! ( m & 0x1 ) )
continue ;
if ( l ) {
strcat ( out , " or " ) ;
l + = 4 ;
}
2016-02-24 11:46:50 +03:00
l + = scnprintf ( out + l , sz - l , tlb_access [ i ] ) ;
2016-02-24 11:46:46 +03:00
}
if ( * out = = ' \0 ' )
2016-02-24 11:46:50 +03:00
l + = scnprintf ( out , sz - l , " N/A " ) ;
2016-02-24 11:46:46 +03:00
if ( hit )
2016-02-24 11:46:50 +03:00
l + = scnprintf ( out + l , sz - l , " hit " ) ;
2016-02-24 11:46:46 +03:00
if ( miss )
2016-02-24 11:46:50 +03:00
l + = scnprintf ( out + l , sz - l , " miss " ) ;
return l ;
2016-02-24 11:46:46 +03:00
}
2016-02-24 11:46:47 +03:00
static const char * const mem_lvl [ ] = {
" N/A " ,
" HIT " ,
" MISS " ,
" L1 " ,
" LFB " ,
" L2 " ,
" L3 " ,
" Local RAM " ,
" Remote RAM (1 hop) " ,
" Remote RAM (2 hops) " ,
" Remote Cache (1 hop) " ,
" Remote Cache (2 hops) " ,
" I/O " ,
" Uncached " ,
} ;
void perf_mem__lvl_scnprintf ( char * out , size_t sz , struct mem_info * mem_info )
{
size_t i , l = 0 ;
u64 m = PERF_MEM_LVL_NA ;
u64 hit , miss ;
if ( mem_info )
m = mem_info - > data_src . mem_lvl ;
sz - = 1 ; /* -1 for null termination */
out [ 0 ] = ' \0 ' ;
hit = m & PERF_MEM_LVL_HIT ;
miss = m & PERF_MEM_LVL_MISS ;
/* already taken care of */
m & = ~ ( PERF_MEM_LVL_HIT | PERF_MEM_LVL_MISS ) ;
for ( i = 0 ; m & & i < ARRAY_SIZE ( mem_lvl ) ; i + + , m > > = 1 ) {
if ( ! ( m & 0x1 ) )
continue ;
if ( l ) {
strcat ( out , " or " ) ;
l + = 4 ;
}
strncat ( out , mem_lvl [ i ] , sz - l ) ;
l + = strlen ( mem_lvl [ i ] ) ;
}
if ( * out = = ' \0 ' )
strcpy ( out , " N/A " ) ;
if ( hit )
strncat ( out , " hit " , sz - l ) ;
if ( miss )
strncat ( out , " miss " , sz - l ) ;
}
2016-02-24 11:46:48 +03:00
static const char * const snoop_access [ ] = {
" N/A " ,
" None " ,
" Miss " ,
" Hit " ,
" HitM " ,
} ;
void perf_mem__snp_scnprintf ( char * out , size_t sz , struct mem_info * mem_info )
{
size_t i , l = 0 ;
u64 m = PERF_MEM_SNOOP_NA ;
sz - = 1 ; /* -1 for null termination */
out [ 0 ] = ' \0 ' ;
if ( mem_info )
m = mem_info - > data_src . mem_snoop ;
for ( i = 0 ; m & & i < ARRAY_SIZE ( snoop_access ) ; i + + , m > > = 1 ) {
if ( ! ( m & 0x1 ) )
continue ;
if ( l ) {
strcat ( out , " or " ) ;
l + = 4 ;
}
strncat ( out , snoop_access [ i ] , sz - l ) ;
l + = strlen ( snoop_access [ i ] ) ;
}
if ( * out = = ' \0 ' )
strcpy ( out , " N/A " ) ;
}
2016-02-24 11:46:49 +03:00
void perf_mem__lck_scnprintf ( char * out , size_t sz __maybe_unused ,
struct mem_info * mem_info )
{
u64 mask = PERF_MEM_LOCK_NA ;
if ( mem_info )
mask = mem_info - > data_src . mem_lock ;
if ( mask & PERF_MEM_LOCK_NA )
strncat ( out , " N/A " , 3 ) ;
else if ( mask & PERF_MEM_LOCK_LOCKED )
strncat ( out , " Yes " , 3 ) ;
else
strncat ( out , " No " , 2 ) ;
}