2010-02-03 16:52:04 -02:00
/*
* build - id . c
*
* build - id support
*
* Copyright ( C ) 2009 , 2010 Red Hat Inc .
* Copyright ( C ) 2009 , 2010 Arnaldo Carvalho de Melo < acme @ redhat . com >
*/
2010-05-20 12:15:33 -03:00
# include "util.h"
# include <stdio.h>
2010-02-03 16:52:04 -02:00
# include "build-id.h"
# include "event.h"
# include "symbol.h"
# include <linux/kernel.h>
2010-07-30 18:28:42 -03:00
# include "debug.h"
2011-11-25 08:19:45 -02:00
# include "session.h"
2011-11-28 08:30:20 -02:00
# include "tool.h"
2014-11-04 10:14:30 +09:00
# include "header.h"
# include "vdso.h"
2010-02-03 16:52:04 -02:00
2014-11-07 22:57:56 +09:00
static bool no_buildid_cache ;
2012-08-07 16:56:05 +04:00
int build_id__mark_dso_hit ( struct perf_tool * tool __maybe_unused ,
union perf_event * event ,
2013-08-27 11:23:06 +03:00
struct perf_sample * sample ,
2012-08-07 16:56:05 +04:00
struct perf_evsel * evsel __maybe_unused ,
struct machine * machine )
2010-02-03 16:52:04 -02:00
{
struct addr_location al ;
u8 cpumode = event - > header . misc & PERF_RECORD_MISC_CPUMODE_MASK ;
2013-08-27 11:23:06 +03:00
struct thread * thread = machine__findnew_thread ( machine , sample - > pid ,
2014-05-12 09:56:42 +09:00
sample - > tid ) ;
2010-02-03 16:52:04 -02:00
if ( thread = = NULL ) {
pr_err ( " problem processing %d event, skipping it. \n " ,
event - > header . type ) ;
return - 1 ;
}
2014-10-23 12:50:25 -03:00
thread__find_addr_map ( thread , cpumode , MAP__FUNCTION , sample - > ip , & al ) ;
2010-02-03 16:52:04 -02:00
if ( al . map ! = NULL )
al . map - > dso - > hit = 1 ;
return 0 ;
}
2012-09-11 01:15:03 +03:00
static int perf_event__exit_del_thread ( struct perf_tool * tool __maybe_unused ,
2011-11-25 08:19:45 -02:00
union perf_event * event ,
2012-09-11 01:15:03 +03:00
struct perf_sample * sample
__maybe_unused ,
2011-11-28 07:56:39 -02:00
struct machine * machine )
2010-07-30 18:28:42 -03:00
{
2013-08-27 11:23:03 +03:00
struct thread * thread = machine__findnew_thread ( machine ,
event - > fork . pid ,
event - > fork . tid ) ;
2010-07-30 18:28:42 -03:00
2011-01-29 14:01:45 -02:00
dump_printf ( " (%d:%d):(%d:%d) \n " , event - > fork . pid , event - > fork . tid ,
event - > fork . ppid , event - > fork . ptid ) ;
2010-07-30 18:28:42 -03:00
if ( thread ) {
2011-11-28 07:56:39 -02:00
rb_erase ( & thread - > rb_node , & machine - > threads ) ;
machine - > last_match = NULL ;
2010-07-30 18:28:42 -03:00
thread__delete ( thread ) ;
}
return 0 ;
}
2011-11-28 08:30:20 -02:00
struct perf_tool build_id__mark_dso_hit_ops = {
2010-02-03 16:52:04 -02:00
. sample = build_id__mark_dso_hit ,
2011-01-29 14:01:45 -02:00
. mmap = perf_event__process_mmap ,
2013-08-21 12:10:25 +02:00
. mmap2 = perf_event__process_mmap2 ,
2012-10-06 15:44:59 -03:00
. fork = perf_event__process_fork ,
2011-01-29 14:01:45 -02:00
. exit = perf_event__exit_del_thread ,
2012-05-15 13:28:15 +02:00
. attr = perf_event__process_attr ,
. build_id = perf_event__process_build_id ,
2010-02-03 16:52:04 -02:00
} ;
2010-05-20 12:15:33 -03:00
2012-10-27 23:18:28 +02:00
int build_id__sprintf ( const u8 * build_id , int len , char * bf )
{
char * bid = bf ;
const u8 * raw = build_id ;
int i ;
for ( i = 0 ; i < len ; + + i ) {
sprintf ( bid , " %02x " , * raw ) ;
+ + raw ;
bid + = 2 ;
}
return raw - build_id ;
}
2015-02-10 18:18:53 +09:00
/* asnprintf consolidates asprintf and snprintf */
static int asnprintf ( char * * strp , size_t size , const char * fmt , . . . )
{
va_list ap ;
int ret ;
if ( ! strp )
return - EINVAL ;
va_start ( ap , fmt ) ;
if ( * strp )
ret = vsnprintf ( * strp , size , fmt , ap ) ;
else
ret = vasprintf ( strp , fmt , ap ) ;
va_end ( ap ) ;
return ret ;
}
static char * build_id__filename ( const char * sbuild_id , char * bf , size_t size )
{
char * tmp = bf ;
int ret = asnprintf ( & bf , size , " %s/.build-id/%.2s/%s " , buildid_dir ,
sbuild_id , sbuild_id + 2 ) ;
if ( ret < 0 | | ( tmp & & size < ( unsigned int ) ret ) )
return NULL ;
return bf ;
}
2013-12-10 15:46:29 -03:00
char * dso__build_id_filename ( const struct dso * dso , char * bf , size_t size )
2010-05-20 12:15:33 -03:00
{
char build_id_hex [ BUILD_ID_SIZE * 2 + 1 ] ;
2013-10-22 19:01:31 -03:00
if ( ! dso - > has_build_id )
2010-05-20 12:15:33 -03:00
return NULL ;
2013-10-22 19:01:31 -03:00
build_id__sprintf ( dso - > build_id , sizeof ( dso - > build_id ) , build_id_hex ) ;
2015-02-10 18:18:53 +09:00
return build_id__filename ( build_id_hex , bf , size ) ;
2010-05-20 12:15:33 -03:00
}
2014-11-04 10:14:30 +09:00
# define dsos__for_each_with_build_id(pos, head) \
list_for_each_entry ( pos , head , node ) \
if ( ! pos - > has_build_id ) \
continue ; \
else
static int write_buildid ( const char * name , size_t name_len , u8 * build_id ,
pid_t pid , u16 misc , int fd )
{
int err ;
struct build_id_event b ;
size_t len ;
len = name_len + 1 ;
len = PERF_ALIGN ( len , NAME_ALIGN ) ;
memset ( & b , 0 , sizeof ( b ) ) ;
memcpy ( & b . build_id , build_id , BUILD_ID_SIZE ) ;
b . pid = pid ;
b . header . misc = misc ;
b . header . size = sizeof ( b ) + len ;
err = writen ( fd , & b , sizeof ( b ) ) ;
if ( err < 0 )
return err ;
return write_padded ( fd , name , name_len + 1 , len ) ;
}
static int __dsos__write_buildid_table ( struct list_head * head ,
struct machine * machine ,
pid_t pid , u16 misc , int fd )
{
char nm [ PATH_MAX ] ;
struct dso * pos ;
dsos__for_each_with_build_id ( pos , head ) {
int err ;
const char * name ;
size_t name_len ;
if ( ! pos - > hit )
continue ;
if ( dso__is_vdso ( pos ) ) {
name = pos - > short_name ;
name_len = pos - > short_name_len + 1 ;
} else if ( dso__is_kcore ( pos ) ) {
machine__mmap_name ( machine , nm , sizeof ( nm ) ) ;
name = nm ;
name_len = strlen ( nm ) + 1 ;
} else {
name = pos - > long_name ;
name_len = pos - > long_name_len + 1 ;
}
err = write_buildid ( name , name_len , pos - > build_id ,
pid , misc , fd ) ;
if ( err )
return err ;
}
return 0 ;
}
static int machine__write_buildid_table ( struct machine * machine , int fd )
{
int err ;
u16 kmisc = PERF_RECORD_MISC_KERNEL ,
umisc = PERF_RECORD_MISC_USER ;
if ( ! machine__is_host ( machine ) ) {
kmisc = PERF_RECORD_MISC_GUEST_KERNEL ;
umisc = PERF_RECORD_MISC_GUEST_USER ;
}
err = __dsos__write_buildid_table ( & machine - > kernel_dsos . head , machine ,
machine - > pid , kmisc , fd ) ;
if ( err = = 0 )
err = __dsos__write_buildid_table ( & machine - > user_dsos . head ,
machine , machine - > pid , umisc ,
fd ) ;
return err ;
}
int perf_session__write_buildid_table ( struct perf_session * session , int fd )
{
struct rb_node * nd ;
int err = machine__write_buildid_table ( & session - > machines . host , fd ) ;
if ( err )
return err ;
for ( nd = rb_first ( & session - > machines . guests ) ; nd ; nd = rb_next ( nd ) ) {
struct machine * pos = rb_entry ( nd , struct machine , rb_node ) ;
err = machine__write_buildid_table ( pos , fd ) ;
if ( err )
break ;
}
return err ;
}
static int __dsos__hit_all ( struct list_head * head )
{
struct dso * pos ;
list_for_each_entry ( pos , head , node )
pos - > hit = true ;
return 0 ;
}
static int machine__hit_all_dsos ( struct machine * machine )
{
int err ;
err = __dsos__hit_all ( & machine - > kernel_dsos . head ) ;
if ( err )
return err ;
return __dsos__hit_all ( & machine - > user_dsos . head ) ;
}
int dsos__hit_all ( struct perf_session * session )
{
struct rb_node * nd ;
int err ;
err = machine__hit_all_dsos ( & session - > machines . host ) ;
if ( err )
return err ;
for ( nd = rb_first ( & session - > machines . guests ) ; nd ; nd = rb_next ( nd ) ) {
struct machine * pos = rb_entry ( nd , struct machine , rb_node ) ;
err = machine__hit_all_dsos ( pos ) ;
if ( err )
return err ;
}
return 0 ;
}
2014-11-07 22:57:56 +09:00
void disable_buildid_cache ( void )
{
no_buildid_cache = true ;
}
2015-02-10 18:18:51 +09:00
int build_id_cache__add_s ( const char * sbuild_id , const char * name ,
bool is_kallsyms , bool is_vdso )
2014-11-04 10:14:30 +09:00
{
const size_t size = PATH_MAX ;
char * realname , * filename = zalloc ( size ) ,
2015-02-10 18:18:53 +09:00
* linkname = zalloc ( size ) , * targetname , * tmp ;
2014-11-04 10:14:30 +09:00
int len , err = - 1 ;
bool slash = is_kallsyms | | is_vdso ;
if ( is_kallsyms ) {
if ( symbol_conf . kptr_restrict ) {
pr_debug ( " Not caching a kptr_restrict'ed /proc/kallsyms \n " ) ;
err = 0 ;
goto out_free ;
}
realname = ( char * ) name ;
} else
realname = realpath ( name , NULL ) ;
if ( realname = = NULL | | filename = = NULL | | linkname = = NULL )
goto out_free ;
len = scnprintf ( filename , size , " %s%s%s " ,
2015-02-10 18:18:51 +09:00
buildid_dir , slash ? " / " : " " ,
2014-11-04 10:14:30 +09:00
is_vdso ? DSO__NAME_VDSO : realname ) ;
if ( mkdir_p ( filename , 0755 ) )
goto out_free ;
snprintf ( filename + len , size - len , " /%s " , sbuild_id ) ;
if ( access ( filename , F_OK ) ) {
if ( is_kallsyms ) {
if ( copyfile ( " /proc/kallsyms " , filename ) )
goto out_free ;
} else if ( link ( realname , filename ) & & copyfile ( name , filename ) )
goto out_free ;
}
2015-02-10 18:18:53 +09:00
if ( ! build_id__filename ( sbuild_id , linkname , size ) )
goto out_free ;
tmp = strrchr ( linkname , ' / ' ) ;
* tmp = ' \0 ' ;
2014-11-04 10:14:30 +09:00
if ( access ( linkname , X_OK ) & & mkdir_p ( linkname , 0755 ) )
goto out_free ;
2015-02-10 18:18:53 +09:00
* tmp = ' / ' ;
2015-02-10 18:18:51 +09:00
targetname = filename + strlen ( buildid_dir ) - 5 ;
2014-11-04 10:14:30 +09:00
memcpy ( targetname , " ../.. " , 5 ) ;
if ( symlink ( targetname , linkname ) = = 0 )
err = 0 ;
out_free :
if ( ! is_kallsyms )
free ( realname ) ;
free ( filename ) ;
free ( linkname ) ;
return err ;
}
static int build_id_cache__add_b ( const u8 * build_id , size_t build_id_size ,
2015-02-10 18:18:51 +09:00
const char * name , bool is_kallsyms ,
bool is_vdso )
2014-11-04 10:14:30 +09:00
{
char sbuild_id [ BUILD_ID_SIZE * 2 + 1 ] ;
build_id__sprintf ( build_id , build_id_size , sbuild_id ) ;
2015-02-10 18:18:51 +09:00
return build_id_cache__add_s ( sbuild_id , name , is_kallsyms , is_vdso ) ;
2014-11-04 10:14:30 +09:00
}
2015-02-10 18:18:51 +09:00
int build_id_cache__remove_s ( const char * sbuild_id )
2014-11-04 10:14:30 +09:00
{
const size_t size = PATH_MAX ;
char * filename = zalloc ( size ) ,
2015-02-10 18:18:53 +09:00
* linkname = zalloc ( size ) , * tmp ;
2014-11-04 10:14:30 +09:00
int err = - 1 ;
if ( filename = = NULL | | linkname = = NULL )
goto out_free ;
2015-02-10 18:18:53 +09:00
if ( ! build_id__filename ( sbuild_id , linkname , size ) )
goto out_free ;
2014-11-04 10:14:30 +09:00
if ( access ( linkname , F_OK ) )
goto out_free ;
if ( readlink ( linkname , filename , size - 1 ) < 0 )
goto out_free ;
if ( unlink ( linkname ) )
goto out_free ;
/*
* Since the link is relative , we must make it absolute :
*/
2015-02-10 18:18:53 +09:00
tmp = strrchr ( linkname , ' / ' ) + 1 ;
snprintf ( tmp , size - ( tmp - linkname ) , " %s " , filename ) ;
2014-11-04 10:14:30 +09:00
if ( unlink ( linkname ) )
goto out_free ;
err = 0 ;
out_free :
free ( filename ) ;
free ( linkname ) ;
return err ;
}
2015-02-10 18:18:51 +09:00
static int dso__cache_build_id ( struct dso * dso , struct machine * machine )
2014-11-04 10:14:30 +09:00
{
bool is_kallsyms = dso - > kernel & & dso - > long_name [ 0 ] ! = ' / ' ;
bool is_vdso = dso__is_vdso ( dso ) ;
const char * name = dso - > long_name ;
char nm [ PATH_MAX ] ;
if ( dso__is_kcore ( dso ) ) {
is_kallsyms = true ;
machine__mmap_name ( machine , nm , sizeof ( nm ) ) ;
name = nm ;
}
return build_id_cache__add_b ( dso - > build_id , sizeof ( dso - > build_id ) , name ,
2015-02-10 18:18:51 +09:00
is_kallsyms , is_vdso ) ;
2014-11-04 10:14:30 +09:00
}
static int __dsos__cache_build_ids ( struct list_head * head ,
2015-02-10 18:18:51 +09:00
struct machine * machine )
2014-11-04 10:14:30 +09:00
{
struct dso * pos ;
int err = 0 ;
dsos__for_each_with_build_id ( pos , head )
2015-02-10 18:18:51 +09:00
if ( dso__cache_build_id ( pos , machine ) )
2014-11-04 10:14:30 +09:00
err = - 1 ;
return err ;
}
2015-02-10 18:18:51 +09:00
static int machine__cache_build_ids ( struct machine * machine )
2014-11-04 10:14:30 +09:00
{
2015-02-10 18:18:51 +09:00
int ret = __dsos__cache_build_ids ( & machine - > kernel_dsos . head , machine ) ;
ret | = __dsos__cache_build_ids ( & machine - > user_dsos . head , machine ) ;
2014-11-04 10:14:30 +09:00
return ret ;
}
int perf_session__cache_build_ids ( struct perf_session * session )
{
struct rb_node * nd ;
int ret ;
2014-11-07 22:57:56 +09:00
if ( no_buildid_cache )
return 0 ;
2014-12-01 20:06:23 +01:00
if ( mkdir ( buildid_dir , 0755 ) ! = 0 & & errno ! = EEXIST )
2014-11-04 10:14:30 +09:00
return - 1 ;
2015-02-10 18:18:51 +09:00
ret = machine__cache_build_ids ( & session - > machines . host ) ;
2014-11-04 10:14:30 +09:00
for ( nd = rb_first ( & session - > machines . guests ) ; nd ; nd = rb_next ( nd ) ) {
struct machine * pos = rb_entry ( nd , struct machine , rb_node ) ;
2015-02-10 18:18:51 +09:00
ret | = machine__cache_build_ids ( pos ) ;
2014-11-04 10:14:30 +09:00
}
return ret ? - 1 : 0 ;
}
static bool machine__read_build_ids ( struct machine * machine , bool with_hits )
{
bool ret ;
ret = __dsos__read_build_ids ( & machine - > kernel_dsos . head , with_hits ) ;
ret | = __dsos__read_build_ids ( & machine - > user_dsos . head , with_hits ) ;
return ret ;
}
bool perf_session__read_build_ids ( struct perf_session * session , bool with_hits )
{
struct rb_node * nd ;
bool ret = machine__read_build_ids ( & session - > machines . host , with_hits ) ;
for ( nd = rb_first ( & session - > machines . guests ) ; nd ; nd = rb_next ( nd ) ) {
struct machine * pos = rb_entry ( nd , struct machine , rb_node ) ;
ret | = machine__read_build_ids ( pos , with_hits ) ;
}
return ret ;
}