2010-01-20 20:28:45 +03:00
/*
* builtin - buildid - cache . c
*
* Builtin buildid - cache command : Manages build - id cache
*
* Copyright ( C ) 2010 , Red Hat Inc .
* Copyright ( C ) 2010 , Arnaldo Carvalho de Melo < acme @ redhat . com >
*/
2013-10-14 17:57:29 +04:00
# include <sys/types.h>
# include <sys/time.h>
# include <time.h>
# include <dirent.h>
# include <unistd.h>
2010-01-20 20:28:45 +03:00
# include "builtin.h"
# include "perf.h"
# include "util/cache.h"
# include "util/debug.h"
# include "util/header.h"
# include "util/parse-options.h"
# include "util/strlist.h"
2012-10-28 01:18:28 +04:00
# include "util/build-id.h"
2012-12-07 23:28:27 +04:00
# include "util/session.h"
2012-10-28 01:18:29 +04:00
# include "util/symbol.h"
2010-01-20 20:28:45 +03:00
2013-10-14 17:57:29 +04:00
static int build_id_cache__kcore_buildid ( const char * proc_dir , char * sbuildid )
{
char root_dir [ PATH_MAX ] ;
char notes [ PATH_MAX ] ;
u8 build_id [ BUILD_ID_SIZE ] ;
char * p ;
strlcpy ( root_dir , proc_dir , sizeof ( root_dir ) ) ;
p = strrchr ( root_dir , ' / ' ) ;
if ( ! p )
return - 1 ;
* p = ' \0 ' ;
scnprintf ( notes , sizeof ( notes ) , " %s/sys/kernel/notes " , root_dir ) ;
if ( sysfs__read_build_id ( notes , build_id , sizeof ( build_id ) ) )
return - 1 ;
build_id__sprintf ( build_id , sizeof ( build_id ) , sbuildid ) ;
return 0 ;
}
static int build_id_cache__kcore_dir ( char * dir , size_t sz )
{
struct timeval tv ;
struct tm tm ;
char dt [ 32 ] ;
if ( gettimeofday ( & tv , NULL ) | | ! localtime_r ( & tv . tv_sec , & tm ) )
return - 1 ;
if ( ! strftime ( dt , sizeof ( dt ) , " %Y%m%d%H%M%S " , & tm ) )
return - 1 ;
scnprintf ( dir , sz , " %s%02u " , dt , ( unsigned ) tv . tv_usec / 10000 ) ;
return 0 ;
}
2014-01-29 18:14:44 +04:00
static bool same_kallsyms_reloc ( const char * from_dir , char * to_dir )
{
char from [ PATH_MAX ] ;
char to [ PATH_MAX ] ;
const char * name ;
u64 addr1 = 0 , addr2 = 0 ;
int i ;
scnprintf ( from , sizeof ( from ) , " %s/kallsyms " , from_dir ) ;
scnprintf ( to , sizeof ( to ) , " %s/kallsyms " , to_dir ) ;
for ( i = 0 ; ( name = ref_reloc_sym_names [ i ] ) ! = NULL ; i + + ) {
addr1 = kallsyms__get_function_start ( from , name ) ;
if ( addr1 )
break ;
}
if ( name )
addr2 = kallsyms__get_function_start ( to , name ) ;
return addr1 = = addr2 ;
}
2013-10-14 17:57:29 +04:00
static int build_id_cache__kcore_existing ( const char * from_dir , char * to_dir ,
size_t to_dir_sz )
{
char from [ PATH_MAX ] ;
char to [ PATH_MAX ] ;
2014-01-29 18:14:44 +04:00
char to_subdir [ PATH_MAX ] ;
2013-10-14 17:57:29 +04:00
struct dirent * dent ;
int ret = - 1 ;
DIR * d ;
d = opendir ( to_dir ) ;
if ( ! d )
return - 1 ;
scnprintf ( from , sizeof ( from ) , " %s/modules " , from_dir ) ;
while ( 1 ) {
dent = readdir ( d ) ;
if ( ! dent )
break ;
if ( dent - > d_type ! = DT_DIR )
continue ;
scnprintf ( to , sizeof ( to ) , " %s/%s/modules " , to_dir ,
dent - > d_name ) ;
2014-01-29 18:14:44 +04:00
scnprintf ( to_subdir , sizeof ( to_subdir ) , " %s/%s " ,
to_dir , dent - > d_name ) ;
if ( ! compare_proc_modules ( from , to ) & &
same_kallsyms_reloc ( from_dir , to_subdir ) ) {
strlcpy ( to_dir , to_subdir , to_dir_sz ) ;
2013-10-14 17:57:29 +04:00
ret = 0 ;
break ;
}
}
closedir ( d ) ;
return ret ;
}
2015-02-10 12:18:51 +03:00
static int build_id_cache__add_kcore ( const char * filename , bool force )
2013-10-14 17:57:29 +04:00
{
char dir [ 32 ] , sbuildid [ BUILD_ID_SIZE * 2 + 1 ] ;
char from_dir [ PATH_MAX ] , to_dir [ PATH_MAX ] ;
char * p ;
strlcpy ( from_dir , filename , sizeof ( from_dir ) ) ;
p = strrchr ( from_dir , ' / ' ) ;
if ( ! p | | strcmp ( p + 1 , " kcore " ) )
return - 1 ;
* p = ' \0 ' ;
if ( build_id_cache__kcore_buildid ( from_dir , sbuildid ) )
return - 1 ;
scnprintf ( to_dir , sizeof ( to_dir ) , " %s/[kernel.kcore]/%s " ,
2015-02-10 12:18:51 +03:00
buildid_dir , sbuildid ) ;
2013-10-14 17:57:29 +04:00
2014-07-14 14:02:38 +04:00
if ( ! force & &
! build_id_cache__kcore_existing ( from_dir , to_dir , sizeof ( to_dir ) ) ) {
2013-10-14 17:57:29 +04:00
pr_debug ( " same kcore found in %s \n " , to_dir ) ;
return 0 ;
}
if ( build_id_cache__kcore_dir ( dir , sizeof ( dir ) ) )
return - 1 ;
scnprintf ( to_dir , sizeof ( to_dir ) , " %s/[kernel.kcore]/%s/%s " ,
2015-02-10 12:18:51 +03:00
buildid_dir , sbuildid , dir ) ;
2013-10-14 17:57:29 +04:00
if ( mkdir_p ( to_dir , 0755 ) )
return - 1 ;
if ( kcore_copy ( from_dir , to_dir ) ) {
/* Remove YYYYmmddHHMMSShh directory */
if ( ! rmdir ( to_dir ) ) {
p = strrchr ( to_dir , ' / ' ) ;
if ( p )
* p = ' \0 ' ;
/* Try to remove buildid directory */
if ( ! rmdir ( to_dir ) ) {
p = strrchr ( to_dir , ' / ' ) ;
if ( p )
* p = ' \0 ' ;
/* Try to remove [kernel.kcore] directory */
rmdir ( to_dir ) ;
}
}
return - 1 ;
}
pr_debug ( " kcore added to build-id cache directory %s \n " , to_dir ) ;
return 0 ;
}
2015-02-10 12:18:51 +03:00
static int build_id_cache__add_file ( const char * filename )
2010-01-20 20:28:45 +03:00
{
char sbuild_id [ BUILD_ID_SIZE * 2 + 1 ] ;
u8 build_id [ BUILD_ID_SIZE ] ;
int err ;
if ( filename__read_build_id ( filename , & build_id , sizeof ( build_id ) ) < 0 ) {
pr_debug ( " Couldn't read a build-id in %s \n " , filename ) ;
return - 1 ;
}
build_id__sprintf ( build_id , sizeof ( build_id ) , sbuild_id ) ;
2015-02-10 12:18:51 +03:00
err = build_id_cache__add_s ( sbuild_id , filename ,
2012-09-10 20:50:19 +04:00
false , false ) ;
2010-01-20 20:28:45 +03:00
if ( verbose )
pr_info ( " Adding %s %s: %s \n " , sbuild_id , filename ,
err ? " FAIL " : " Ok " ) ;
return err ;
}
2015-02-10 12:18:51 +03:00
static int build_id_cache__remove_file ( const char * filename )
2010-01-20 20:28:45 +03:00
{
u8 build_id [ BUILD_ID_SIZE ] ;
char sbuild_id [ BUILD_ID_SIZE * 2 + 1 ] ;
int err ;
if ( filename__read_build_id ( filename , & build_id , sizeof ( build_id ) ) < 0 ) {
pr_debug ( " Couldn't read a build-id in %s \n " , filename ) ;
return - 1 ;
}
build_id__sprintf ( build_id , sizeof ( build_id ) , sbuild_id ) ;
2015-02-10 12:18:51 +03:00
err = build_id_cache__remove_s ( sbuild_id ) ;
2010-01-20 20:28:45 +03:00
if ( verbose )
pr_info ( " Removing %s %s: %s \n " , sbuild_id , filename ,
err ? " FAIL " : " Ok " ) ;
return err ;
}
2015-02-27 07:50:26 +03:00
static int build_id_cache__purge_path ( const char * pathname )
{
struct strlist * list ;
struct str_node * pos ;
int err ;
err = build_id_cache__list_build_ids ( pathname , & list ) ;
if ( err )
goto out ;
strlist__for_each ( pos , list ) {
err = build_id_cache__remove_s ( pos - > s ) ;
if ( verbose )
pr_info ( " Removing %s %s: %s \n " , pos - > s , pathname ,
err ? " FAIL " : " Ok " ) ;
if ( err )
break ;
}
strlist__delete ( list ) ;
out :
if ( verbose )
pr_info ( " Purging %s: %s \n " , pathname , err ? " FAIL " : " Ok " ) ;
return err ;
}
2012-12-07 23:28:27 +04:00
static bool dso__missing_buildid_cache ( struct dso * dso , int parm __maybe_unused )
{
char filename [ PATH_MAX ] ;
u8 build_id [ BUILD_ID_SIZE ] ;
if ( dso__build_id_filename ( dso , filename , sizeof ( filename ) ) & &
filename__read_build_id ( filename , build_id ,
sizeof ( build_id ) ) ! = sizeof ( build_id ) ) {
if ( errno = = ENOENT )
return false ;
2014-12-17 23:24:45 +03:00
pr_warning ( " Problems with %s file, consider removing it from the cache \n " ,
2012-12-07 23:28:27 +04:00
filename ) ;
} else if ( memcmp ( dso - > build_id , build_id , sizeof ( dso - > build_id ) ) ) {
2014-12-17 23:24:45 +03:00
pr_warning ( " Problems with %s file, consider removing it from the cache \n " ,
2012-12-07 23:28:27 +04:00
filename ) ;
}
return true ;
}
2014-08-12 10:40:36 +04:00
static int build_id_cache__fprintf_missing ( struct perf_session * session , FILE * fp )
2012-12-07 23:28:27 +04:00
{
perf_session__fprintf_dsos_buildid ( session , fp , dso__missing_buildid_cache , 0 ) ;
return 0 ;
}
2015-02-10 12:18:51 +03:00
static int build_id_cache__update_file ( const char * filename )
2013-02-07 13:02:11 +04:00
{
u8 build_id [ BUILD_ID_SIZE ] ;
char sbuild_id [ BUILD_ID_SIZE * 2 + 1 ] ;
2015-02-26 09:54:40 +03:00
int err = 0 ;
2013-02-07 13:02:11 +04:00
if ( filename__read_build_id ( filename , & build_id , sizeof ( build_id ) ) < 0 ) {
pr_debug ( " Couldn't read a build-id in %s \n " , filename ) ;
return - 1 ;
}
build_id__sprintf ( build_id , sizeof ( build_id ) , sbuild_id ) ;
2015-02-26 09:54:40 +03:00
if ( build_id_cache__cached ( sbuild_id ) )
err = build_id_cache__remove_s ( sbuild_id ) ;
2015-02-10 12:18:51 +03:00
if ( ! err )
err = build_id_cache__add_s ( sbuild_id , filename , false , false ) ;
2013-02-07 13:02:11 +04:00
if ( verbose )
pr_info ( " Updating %s %s: %s \n " , sbuild_id , filename ,
err ? " FAIL " : " Ok " ) ;
return err ;
}
2012-10-01 22:20:58 +04:00
int cmd_buildid_cache ( int argc , const char * * argv ,
const char * prefix __maybe_unused )
2010-01-20 20:28:45 +03:00
{
struct strlist * list ;
struct str_node * pos ;
2012-12-07 23:28:27 +04:00
int ret = 0 ;
bool force = false ;
2012-10-01 22:20:58 +04:00
char const * add_name_list_str = NULL ,
2012-12-07 23:28:27 +04:00
* remove_name_list_str = NULL ,
2015-02-27 07:50:26 +03:00
* purge_name_list_str = NULL ,
2013-02-07 13:02:11 +04:00
* missing_filename = NULL ,
2013-10-14 17:57:29 +04:00
* update_name_list_str = NULL ,
2014-12-01 22:06:27 +03:00
* kcore_filename = NULL ;
2014-08-14 06:22:49 +04:00
char sbuf [ STRERR_BUFSIZE ] ;
2013-02-07 13:02:11 +04:00
2014-08-12 10:40:36 +04:00
struct perf_data_file file = {
. mode = PERF_DATA_MODE_READ ,
} ;
struct perf_session * session = NULL ;
2012-10-01 22:20:58 +04:00
const struct option buildid_cache_options [ ] = {
OPT_STRING ( ' a ' , " add " , & add_name_list_str ,
" file list " , " file(s) to add " ) ,
2013-10-14 17:57:29 +04:00
OPT_STRING ( ' k ' , " kcore " , & kcore_filename ,
" file " , " kcore file to add " ) ,
2012-10-01 22:20:58 +04:00
OPT_STRING ( ' r ' , " remove " , & remove_name_list_str , " file list " ,
" file(s) to remove " ) ,
2015-02-27 07:50:26 +03:00
OPT_STRING ( ' p ' , " purge " , & purge_name_list_str , " path list " ,
" path(s) to remove (remove old caches too) " ) ,
2012-12-07 23:28:27 +04:00
OPT_STRING ( ' M ' , " missing " , & missing_filename , " file " ,
" to find missing build ids in the cache " ) ,
OPT_BOOLEAN ( ' f ' , " force " , & force , " don't complain, do it " ) ,
2013-02-07 13:02:11 +04:00
OPT_STRING ( ' u ' , " update " , & update_name_list_str , " file list " ,
" file(s) to update " ) ,
2012-10-01 22:20:58 +04:00
OPT_INCR ( ' v ' , " verbose " , & verbose , " be more verbose " ) ,
OPT_END ( )
} ;
const char * const buildid_cache_usage [ ] = {
" perf buildid-cache [<options>] " ,
NULL
} ;
argc = parse_options ( argc , argv , buildid_cache_options ,
buildid_cache_usage , 0 ) ;
2014-08-12 10:40:36 +04:00
if ( missing_filename ) {
file . path = missing_filename ;
file . force = force ;
session = perf_session__new ( & file , false , NULL ) ;
if ( session = = NULL )
return - 1 ;
}
2014-08-12 10:40:45 +04:00
if ( symbol__init ( session ? & session - > header . env : NULL ) < 0 )
2014-08-12 10:40:36 +04:00
goto out ;
2012-10-01 22:20:58 +04:00
setup_pager ( ) ;
2010-01-20 20:28:45 +03:00
if ( add_name_list_str ) {
list = strlist__new ( true , add_name_list_str ) ;
if ( list ) {
strlist__for_each ( pos , list )
2015-02-10 12:18:51 +03:00
if ( build_id_cache__add_file ( pos - > s ) ) {
2010-01-20 20:28:45 +03:00
if ( errno = = EEXIST ) {
pr_debug ( " %s already in the cache \n " ,
pos - > s ) ;
continue ;
}
pr_warning ( " Couldn't add %s: %s \n " ,
2014-08-14 06:22:49 +04:00
pos - > s , strerror_r ( errno , sbuf , sizeof ( sbuf ) ) ) ;
2010-01-20 20:28:45 +03:00
}
strlist__delete ( list ) ;
}
}
if ( remove_name_list_str ) {
list = strlist__new ( true , remove_name_list_str ) ;
if ( list ) {
strlist__for_each ( pos , list )
2015-02-10 12:18:51 +03:00
if ( build_id_cache__remove_file ( pos - > s ) ) {
2010-01-20 20:28:45 +03:00
if ( errno = = ENOENT ) {
pr_debug ( " %s wasn't in the cache \n " ,
pos - > s ) ;
continue ;
}
pr_warning ( " Couldn't remove %s: %s \n " ,
2014-08-14 06:22:49 +04:00
pos - > s , strerror_r ( errno , sbuf , sizeof ( sbuf ) ) ) ;
2010-01-20 20:28:45 +03:00
}
strlist__delete ( list ) ;
}
}
2015-02-27 07:50:26 +03:00
if ( purge_name_list_str ) {
list = strlist__new ( true , purge_name_list_str ) ;
if ( list ) {
strlist__for_each ( pos , list )
if ( build_id_cache__purge_path ( pos - > s ) ) {
if ( errno = = ENOENT ) {
pr_debug ( " %s wasn't in the cache \n " ,
pos - > s ) ;
continue ;
}
pr_warning ( " Couldn't remove %s: %s \n " ,
pos - > s , strerror_r ( errno , sbuf , sizeof ( sbuf ) ) ) ;
}
strlist__delete ( list ) ;
}
}
2012-12-07 23:28:27 +04:00
if ( missing_filename )
2014-08-12 10:40:36 +04:00
ret = build_id_cache__fprintf_missing ( session , stdout ) ;
2012-12-07 23:28:27 +04:00
2013-02-07 13:02:11 +04:00
if ( update_name_list_str ) {
list = strlist__new ( true , update_name_list_str ) ;
if ( list ) {
strlist__for_each ( pos , list )
2015-02-10 12:18:51 +03:00
if ( build_id_cache__update_file ( pos - > s ) ) {
2013-02-07 13:02:11 +04:00
if ( errno = = ENOENT ) {
pr_debug ( " %s wasn't in the cache \n " ,
pos - > s ) ;
continue ;
}
pr_warning ( " Couldn't update %s: %s \n " ,
2014-08-14 06:22:49 +04:00
pos - > s , strerror_r ( errno , sbuf , sizeof ( sbuf ) ) ) ;
2013-02-07 13:02:11 +04:00
}
strlist__delete ( list ) ;
}
}
2015-02-10 12:18:51 +03:00
if ( kcore_filename & & build_id_cache__add_kcore ( kcore_filename , force ) )
2013-10-14 17:57:29 +04:00
pr_warning ( " Couldn't add %s \n " , kcore_filename ) ;
2014-08-12 10:40:36 +04:00
out :
if ( session )
perf_session__delete ( session ) ;
2012-12-07 23:28:27 +04:00
return ret ;
2010-01-20 20:28:45 +03:00
}