2019-09-18 22:08:52 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2021-07-20 01:31:52 +03:00
# include "util/cgroup.h"
# include "util/data.h"
2019-09-18 22:08:52 +03:00
# include "util/debug.h"
# include "util/dso.h"
# include "util/event.h"
# include "util/evlist.h"
# include "util/machine.h"
# include "util/map.h"
# include "util/map_symbol.h"
# include "util/branch.h"
# include "util/memswap.h"
# include "util/namespaces.h"
# include "util/session.h"
# include "util/stat.h"
# include "util/symbol.h"
# include "util/synthetic-events.h"
# include "util/target.h"
# include "util/time-utils.h"
# include <linux/bitops.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/zalloc.h>
# include <linux/perf_event.h>
# include <asm/bug.h>
# include <perf/evsel.h>
# include <perf/cpumap.h>
2019-08-06 16:25:25 +03:00
# include <internal/lib.h> // page_size
2019-09-18 22:08:52 +03:00
# include <internal/threadmap.h>
# include <perf/threadmap.h>
# include <symbol/kallsyms.h>
# include <dirent.h>
# include <errno.h>
# include <inttypes.h>
# include <stdio.h>
# include <string.h>
# include <uapi/linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */
# include <api/fs/fs.h>
2020-04-15 08:40:50 +03:00
# include <api/io.h>
2019-09-18 22:08:52 +03:00
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# define DEFAULT_PROC_MAP_PARSE_TIMEOUT 500
unsigned int proc_map_timeout = DEFAULT_PROC_MAP_PARSE_TIMEOUT ;
int perf_tool__process_synth_event ( struct perf_tool * tool ,
union perf_event * event ,
struct machine * machine ,
perf_event__handler_t process )
{
struct perf_sample synth_sample = {
. pid = - 1 ,
. tid = - 1 ,
. time = - 1 ,
. stream_id = - 1 ,
. cpu = - 1 ,
. period = 1 ,
. cpumode = event - > header . misc & PERF_RECORD_MISC_CPUMODE_MASK ,
} ;
return process ( tool , event , & synth_sample , machine ) ;
} ;
/*
* Assumes that the first 4095 bytes of / proc / pid / stat contains
* the comm , tgid and ppid .
*/
2021-02-02 12:01:16 +03:00
static int perf_event__get_comm_ids ( pid_t pid , pid_t tid , char * comm , size_t len ,
2021-02-02 12:01:17 +03:00
pid_t * tgid , pid_t * ppid , bool * kernel )
2019-09-18 22:08:52 +03:00
{
char bf [ 4096 ] ;
int fd ;
size_t size = 0 ;
ssize_t n ;
2021-02-02 12:01:17 +03:00
char * name , * tgids , * ppids , * vmpeak , * threads ;
2019-09-18 22:08:52 +03:00
* tgid = - 1 ;
* ppid = - 1 ;
2021-02-02 12:01:16 +03:00
if ( pid )
snprintf ( bf , sizeof ( bf ) , " /proc/%d/task/%d/status " , pid , tid ) ;
else
snprintf ( bf , sizeof ( bf ) , " /proc/%d/status " , tid ) ;
2019-09-18 22:08:52 +03:00
2020-04-02 18:43:55 +03:00
fd = open ( bf , O_RDONLY ) ;
2019-09-18 22:08:52 +03:00
if ( fd < 0 ) {
2020-04-02 18:43:55 +03:00
pr_debug ( " couldn't open %s \n " , bf ) ;
2019-09-18 22:08:52 +03:00
return - 1 ;
}
n = read ( fd , bf , sizeof ( bf ) - 1 ) ;
close ( fd ) ;
if ( n < = 0 ) {
pr_warning ( " Couldn't get COMM, tigd and ppid for pid %d \n " ,
2021-02-02 12:01:16 +03:00
tid ) ;
2019-09-18 22:08:52 +03:00
return - 1 ;
}
bf [ n ] = ' \0 ' ;
name = strstr ( bf , " Name: " ) ;
2021-02-02 12:01:17 +03:00
tgids = strstr ( name ? : bf , " Tgid: " ) ;
ppids = strstr ( tgids ? : bf , " PPid: " ) ;
vmpeak = strstr ( ppids ? : bf , " VmPeak: " ) ;
if ( vmpeak )
threads = NULL ;
else
threads = strstr ( ppids ? : bf , " Threads: " ) ;
2019-09-18 22:08:52 +03:00
if ( name ) {
char * nl ;
name = skip_spaces ( name + 5 ) ; /* strlen("Name:") */
nl = strchr ( name , ' \n ' ) ;
if ( nl )
* nl = ' \0 ' ;
size = strlen ( name ) ;
if ( size > = len )
size = len - 1 ;
memcpy ( comm , name , size ) ;
comm [ size ] = ' \0 ' ;
} else {
2021-02-02 12:01:16 +03:00
pr_debug ( " Name: string not found for pid %d \n " , tid ) ;
2019-09-18 22:08:52 +03:00
}
if ( tgids ) {
tgids + = 5 ; /* strlen("Tgid:") */
* tgid = atoi ( tgids ) ;
} else {
2021-02-02 12:01:16 +03:00
pr_debug ( " Tgid: string not found for pid %d \n " , tid ) ;
2019-09-18 22:08:52 +03:00
}
if ( ppids ) {
ppids + = 5 ; /* strlen("PPid:") */
* ppid = atoi ( ppids ) ;
} else {
2021-02-02 12:01:16 +03:00
pr_debug ( " PPid: string not found for pid %d \n " , tid ) ;
2019-09-18 22:08:52 +03:00
}
2021-02-02 12:01:17 +03:00
if ( ! vmpeak & & threads )
* kernel = true ;
else
* kernel = false ;
2019-09-18 22:08:52 +03:00
return 0 ;
}
2021-02-02 12:01:16 +03:00
static int perf_event__prepare_comm ( union perf_event * event , pid_t pid , pid_t tid ,
2019-09-18 22:08:52 +03:00
struct machine * machine ,
2021-02-02 12:01:17 +03:00
pid_t * tgid , pid_t * ppid , bool * kernel )
2019-09-18 22:08:52 +03:00
{
size_t size ;
* ppid = - 1 ;
memset ( & event - > comm , 0 , sizeof ( event - > comm ) ) ;
if ( machine__is_host ( machine ) ) {
2021-02-02 12:01:16 +03:00
if ( perf_event__get_comm_ids ( pid , tid , event - > comm . comm ,
2019-09-18 22:08:52 +03:00
sizeof ( event - > comm . comm ) ,
2021-02-02 12:01:17 +03:00
tgid , ppid , kernel ) ! = 0 ) {
2019-09-18 22:08:52 +03:00
return - 1 ;
}
} else {
* tgid = machine - > pid ;
}
if ( * tgid < 0 )
return - 1 ;
event - > comm . pid = * tgid ;
event - > comm . header . type = PERF_RECORD_COMM ;
size = strlen ( event - > comm . comm ) + 1 ;
size = PERF_ALIGN ( size , sizeof ( u64 ) ) ;
memset ( event - > comm . comm + size , 0 , machine - > id_hdr_size ) ;
event - > comm . header . size = ( sizeof ( event - > comm ) -
( sizeof ( event - > comm . comm ) - size ) +
machine - > id_hdr_size ) ;
2021-02-02 12:01:16 +03:00
event - > comm . tid = tid ;
2019-09-18 22:08:52 +03:00
return 0 ;
}
pid_t perf_event__synthesize_comm ( struct perf_tool * tool ,
union perf_event * event , pid_t pid ,
perf_event__handler_t process ,
struct machine * machine )
{
pid_t tgid , ppid ;
2021-02-02 12:01:17 +03:00
bool kernel_thread ;
2019-09-18 22:08:52 +03:00
2021-02-02 12:01:17 +03:00
if ( perf_event__prepare_comm ( event , 0 , pid , machine , & tgid , & ppid ,
& kernel_thread ) ! = 0 )
2019-09-18 22:08:52 +03:00
return - 1 ;
if ( perf_tool__process_synth_event ( tool , event , machine , process ) ! = 0 )
return - 1 ;
return tgid ;
}
static void perf_event__get_ns_link_info ( pid_t pid , const char * ns ,
struct perf_ns_link_info * ns_link_info )
{
struct stat64 st ;
char proc_ns [ 128 ] ;
sprintf ( proc_ns , " /proc/%u/ns/%s " , pid , ns ) ;
if ( stat64 ( proc_ns , & st ) = = 0 ) {
ns_link_info - > dev = st . st_dev ;
ns_link_info - > ino = st . st_ino ;
}
}
int perf_event__synthesize_namespaces ( struct perf_tool * tool ,
union perf_event * event ,
pid_t pid , pid_t tgid ,
perf_event__handler_t process ,
struct machine * machine )
{
u32 idx ;
struct perf_ns_link_info * ns_link_info ;
if ( ! tool | | ! tool - > namespace_events )
return 0 ;
memset ( & event - > namespaces , 0 , ( sizeof ( event - > namespaces ) +
( NR_NAMESPACES * sizeof ( struct perf_ns_link_info ) ) +
machine - > id_hdr_size ) ) ;
event - > namespaces . pid = tgid ;
event - > namespaces . tid = pid ;
event - > namespaces . nr_namespaces = NR_NAMESPACES ;
ns_link_info = event - > namespaces . link_info ;
for ( idx = 0 ; idx < event - > namespaces . nr_namespaces ; idx + + )
perf_event__get_ns_link_info ( pid , perf_ns__name ( idx ) ,
& ns_link_info [ idx ] ) ;
event - > namespaces . header . type = PERF_RECORD_NAMESPACES ;
event - > namespaces . header . size = ( sizeof ( event - > namespaces ) +
( NR_NAMESPACES * sizeof ( struct perf_ns_link_info ) ) +
machine - > id_hdr_size ) ;
if ( perf_tool__process_synth_event ( tool , event , machine , process ) ! = 0 )
return - 1 ;
return 0 ;
}
static int perf_event__synthesize_fork ( struct perf_tool * tool ,
union perf_event * event ,
pid_t pid , pid_t tgid , pid_t ppid ,
perf_event__handler_t process ,
struct machine * machine )
{
memset ( & event - > fork , 0 , sizeof ( event - > fork ) + machine - > id_hdr_size ) ;
/*
* for main thread set parent to ppid from status file . For other
* threads set parent pid to main thread . ie . , assume main thread
* spawns all threads in a process
*/
if ( tgid = = pid ) {
event - > fork . ppid = ppid ;
event - > fork . ptid = ppid ;
} else {
event - > fork . ppid = tgid ;
event - > fork . ptid = tgid ;
}
event - > fork . pid = tgid ;
event - > fork . tid = pid ;
event - > fork . header . type = PERF_RECORD_FORK ;
event - > fork . header . misc = PERF_RECORD_MISC_FORK_EXEC ;
event - > fork . header . size = ( sizeof ( event - > fork ) + machine - > id_hdr_size ) ;
if ( perf_tool__process_synth_event ( tool , event , machine , process ) ! = 0 )
return - 1 ;
return 0 ;
}
2020-04-15 08:40:50 +03:00
static bool read_proc_maps_line ( struct io * io , __u64 * start , __u64 * end ,
u32 * prot , u32 * flags , __u64 * offset ,
u32 * maj , u32 * min ,
__u64 * inode ,
ssize_t pathname_size , char * pathname )
{
__u64 temp ;
int ch ;
char * start_pathname = pathname ;
if ( io__get_hex ( io , start ) ! = ' - ' )
return false ;
if ( io__get_hex ( io , end ) ! = ' ' )
return false ;
/* map protection and flags bits */
* prot = 0 ;
ch = io__get_char ( io ) ;
if ( ch = = ' r ' )
* prot | = PROT_READ ;
else if ( ch ! = ' - ' )
return false ;
ch = io__get_char ( io ) ;
if ( ch = = ' w ' )
* prot | = PROT_WRITE ;
else if ( ch ! = ' - ' )
return false ;
ch = io__get_char ( io ) ;
if ( ch = = ' x ' )
* prot | = PROT_EXEC ;
else if ( ch ! = ' - ' )
return false ;
ch = io__get_char ( io ) ;
if ( ch = = ' s ' )
* flags = MAP_SHARED ;
else if ( ch = = ' p ' )
* flags = MAP_PRIVATE ;
else
return false ;
if ( io__get_char ( io ) ! = ' ' )
return false ;
if ( io__get_hex ( io , offset ) ! = ' ' )
return false ;
if ( io__get_hex ( io , & temp ) ! = ' : ' )
return false ;
* maj = temp ;
if ( io__get_hex ( io , & temp ) ! = ' ' )
return false ;
* min = temp ;
ch = io__get_dec ( io , inode ) ;
if ( ch ! = ' ' ) {
* pathname = ' \0 ' ;
return ch = = ' \n ' ;
}
do {
ch = io__get_char ( io ) ;
} while ( ch = = ' ' ) ;
while ( true ) {
if ( ch < 0 )
return false ;
if ( ch = = ' \0 ' | | ch = = ' \n ' | |
( pathname + 1 - start_pathname ) > = pathname_size ) {
* pathname = ' \0 ' ;
return true ;
}
* pathname + + = ch ;
ch = io__get_char ( io ) ;
}
}
2020-12-14 13:54:52 +03:00
static void perf_record_mmap2__read_build_id ( struct perf_record_mmap2 * event ,
bool is_kernel )
{
struct build_id bid ;
int rc ;
if ( is_kernel )
rc = sysfs__read_build_id ( " /sys/kernel/notes " , & bid ) ;
else
rc = filename__read_build_id ( event - > filename , & bid ) > 0 ? 0 : - 1 ;
if ( rc = = 0 ) {
memcpy ( event - > build_id , bid . data , sizeof ( bid . data ) ) ;
event - > build_id_size = ( u8 ) bid . size ;
event - > header . misc | = PERF_RECORD_MISC_MMAP_BUILD_ID ;
event - > __reserved_1 = 0 ;
event - > __reserved_2 = 0 ;
} else {
if ( event - > filename [ 0 ] = = ' / ' ) {
pr_debug2 ( " Failed to read build ID for %s \n " ,
event - > filename ) ;
}
}
}
2019-09-18 22:08:52 +03:00
int perf_event__synthesize_mmap_events ( struct perf_tool * tool ,
union perf_event * event ,
pid_t pid , pid_t tgid ,
perf_event__handler_t process ,
struct machine * machine ,
bool mmap_data )
{
unsigned long long t ;
2020-04-02 18:43:55 +03:00
char bf [ BUFSIZ ] ;
2020-04-15 08:40:50 +03:00
struct io io ;
2019-09-18 22:08:52 +03:00
bool truncation = false ;
unsigned long long timeout = proc_map_timeout * 1000000ULL ;
int rc = 0 ;
const char * hugetlbfs_mnt = hugetlbfs__mountpoint ( ) ;
int hugetlbfs_mnt_len = hugetlbfs_mnt ? strlen ( hugetlbfs_mnt ) : 0 ;
if ( machine__is_default_guest ( machine ) )
return 0 ;
2020-04-02 18:43:55 +03:00
snprintf ( bf , sizeof ( bf ) , " %s/proc/%d/task/%d/maps " ,
machine - > root_dir , pid , pid ) ;
2019-09-18 22:08:52 +03:00
2020-04-15 08:40:50 +03:00
io . fd = open ( bf , O_RDONLY , 0 ) ;
if ( io . fd < 0 ) {
2019-09-18 22:08:52 +03:00
/*
* We raced with a task exiting - just return :
*/
2020-04-02 18:43:55 +03:00
pr_debug ( " couldn't open %s \n " , bf ) ;
2019-09-18 22:08:52 +03:00
return - 1 ;
}
2020-04-15 08:40:50 +03:00
io__init ( & io , io . fd , bf , sizeof ( bf ) ) ;
2019-09-18 22:08:52 +03:00
event - > header . type = PERF_RECORD_MMAP2 ;
t = rdclock ( ) ;
2020-04-15 08:40:50 +03:00
while ( ! io . eof ) {
static const char anonstr [ ] = " //anon " ;
2021-03-10 02:49:45 +03:00
size_t size , aligned_size ;
2019-09-18 22:08:52 +03:00
2020-04-15 08:40:50 +03:00
/* ensure null termination since stack will be reused. */
event - > mmap2 . filename [ 0 ] = ' \0 ' ;
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
if ( ! read_proc_maps_line ( & io ,
& event - > mmap2 . start ,
& event - > mmap2 . len ,
& event - > mmap2 . prot ,
& event - > mmap2 . flags ,
& event - > mmap2 . pgoff ,
& event - > mmap2 . maj ,
& event - > mmap2 . min ,
& event - > mmap2 . ino ,
sizeof ( event - > mmap2 . filename ) ,
event - > mmap2 . filename ) )
continue ;
2019-09-18 22:08:52 +03:00
if ( ( rdclock ( ) - t ) > timeout ) {
2020-04-02 18:43:55 +03:00
pr_warning ( " Reading %s/proc/%d/task/%d/maps time out. "
2019-09-18 22:08:52 +03:00
" You may want to increase "
" the time limit by --proc-map-timeout \n " ,
2020-04-02 18:43:55 +03:00
machine - > root_dir , pid , pid ) ;
2019-09-18 22:08:52 +03:00
truncation = true ;
goto out ;
}
2020-03-13 08:31:29 +03:00
event - > mmap2 . ino_generation = 0 ;
2019-09-18 22:08:52 +03:00
/*
* Just like the kernel , see __perf_event_mmap in kernel / perf_event . c
*/
if ( machine__is_host ( machine ) )
event - > header . misc = PERF_RECORD_MISC_USER ;
else
event - > header . misc = PERF_RECORD_MISC_GUEST_USER ;
2020-04-15 08:40:50 +03:00
if ( ( event - > mmap2 . prot & PROT_EXEC ) = = 0 ) {
if ( ! mmap_data | | ( event - > mmap2 . prot & PROT_READ ) = = 0 )
2019-09-18 22:08:52 +03:00
continue ;
event - > header . misc | = PERF_RECORD_MISC_MMAP_DATA ;
}
out :
if ( truncation )
event - > header . misc | = PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT ;
2020-04-15 08:40:50 +03:00
if ( ! strcmp ( event - > mmap2 . filename , " " ) )
strcpy ( event - > mmap2 . filename , anonstr ) ;
2019-09-18 22:08:52 +03:00
if ( hugetlbfs_mnt_len & &
2020-04-15 08:40:50 +03:00
! strncmp ( event - > mmap2 . filename , hugetlbfs_mnt ,
hugetlbfs_mnt_len ) ) {
strcpy ( event - > mmap2 . filename , anonstr ) ;
2019-09-18 22:08:52 +03:00
event - > mmap2 . flags | = MAP_HUGETLB ;
}
2020-04-15 08:40:50 +03:00
size = strlen ( event - > mmap2 . filename ) + 1 ;
2021-03-10 02:49:45 +03:00
aligned_size = PERF_ALIGN ( size , sizeof ( u64 ) ) ;
2019-09-18 22:08:52 +03:00
event - > mmap2 . len - = event - > mmap . start ;
event - > mmap2 . header . size = ( sizeof ( event - > mmap2 ) -
2021-03-10 02:49:45 +03:00
( sizeof ( event - > mmap2 . filename ) - aligned_size ) ) ;
memset ( event - > mmap2 . filename + size , 0 , machine - > id_hdr_size +
( aligned_size - size ) ) ;
2019-09-18 22:08:52 +03:00
event - > mmap2 . header . size + = machine - > id_hdr_size ;
event - > mmap2 . pid = tgid ;
event - > mmap2 . tid = pid ;
2020-12-14 13:54:52 +03:00
if ( symbol_conf . buildid_mmap2 )
perf_record_mmap2__read_build_id ( & event - > mmap2 , false ) ;
2019-09-18 22:08:52 +03:00
if ( perf_tool__process_synth_event ( tool , event , machine , process ) ! = 0 ) {
rc = - 1 ;
break ;
}
if ( truncation )
break ;
}
2020-04-15 08:40:50 +03:00
close ( io . fd ) ;
2019-09-18 22:08:52 +03:00
return rc ;
}
2020-03-25 15:45:33 +03:00
# ifdef HAVE_FILE_HANDLE
static int perf_event__synthesize_cgroup ( struct perf_tool * tool ,
union perf_event * event ,
char * path , size_t mount_len ,
perf_event__handler_t process ,
struct machine * machine )
{
size_t event_size = sizeof ( event - > cgroup ) - sizeof ( event - > cgroup . path ) ;
size_t path_len = strlen ( path ) - mount_len + 1 ;
struct {
struct file_handle fh ;
uint64_t cgroup_id ;
} handle ;
int mount_id ;
while ( path_len % sizeof ( u64 ) )
path [ mount_len + path_len + + ] = ' \0 ' ;
memset ( & event - > cgroup , 0 , event_size ) ;
event - > cgroup . header . type = PERF_RECORD_CGROUP ;
event - > cgroup . header . size = event_size + path_len + machine - > id_hdr_size ;
handle . fh . handle_bytes = sizeof ( handle . cgroup_id ) ;
if ( name_to_handle_at ( AT_FDCWD , path , & handle . fh , & mount_id , 0 ) < 0 ) {
pr_debug ( " stat failed: %s \n " , path ) ;
return - 1 ;
}
event - > cgroup . id = handle . cgroup_id ;
strncpy ( event - > cgroup . path , path + mount_len , path_len ) ;
memset ( event - > cgroup . path + path_len , 0 , machine - > id_hdr_size ) ;
if ( perf_tool__process_synth_event ( tool , event , machine , process ) < 0 ) {
pr_debug ( " process synth event failed \n " ) ;
return - 1 ;
}
return 0 ;
}
static int perf_event__walk_cgroup_tree ( struct perf_tool * tool ,
union perf_event * event ,
char * path , size_t mount_len ,
perf_event__handler_t process ,
struct machine * machine )
{
size_t pos = strlen ( path ) ;
DIR * d ;
struct dirent * dent ;
int ret = 0 ;
if ( perf_event__synthesize_cgroup ( tool , event , path , mount_len ,
process , machine ) < 0 )
return - 1 ;
d = opendir ( path ) ;
if ( d = = NULL ) {
pr_debug ( " failed to open directory: %s \n " , path ) ;
return - 1 ;
}
while ( ( dent = readdir ( d ) ) ! = NULL ) {
if ( dent - > d_type ! = DT_DIR )
continue ;
if ( ! strcmp ( dent - > d_name , " . " ) | |
! strcmp ( dent - > d_name , " .. " ) )
continue ;
/* any sane path should be less than PATH_MAX */
if ( strlen ( path ) + strlen ( dent - > d_name ) + 1 > = PATH_MAX )
continue ;
if ( path [ pos - 1 ] ! = ' / ' )
strcat ( path , " / " ) ;
strcat ( path , dent - > d_name ) ;
ret = perf_event__walk_cgroup_tree ( tool , event , path ,
mount_len , process , machine ) ;
if ( ret < 0 )
break ;
path [ pos ] = ' \0 ' ;
}
closedir ( d ) ;
return ret ;
}
int perf_event__synthesize_cgroups ( struct perf_tool * tool ,
perf_event__handler_t process ,
struct machine * machine )
{
union perf_event event ;
char cgrp_root [ PATH_MAX ] ;
size_t mount_len ; /* length of mount point in the path */
2020-11-27 08:43:56 +03:00
if ( ! tool | | ! tool - > cgroup_events )
return 0 ;
2020-03-25 15:45:33 +03:00
if ( cgroupfs_find_mountpoint ( cgrp_root , PATH_MAX , " perf_event " ) < 0 ) {
pr_debug ( " cannot find cgroup mount point \n " ) ;
return - 1 ;
}
mount_len = strlen ( cgrp_root ) ;
/* make sure the path starts with a slash (after mount point) */
strcat ( cgrp_root , " / " ) ;
if ( perf_event__walk_cgroup_tree ( tool , & event , cgrp_root , mount_len ,
process , machine ) < 0 )
return - 1 ;
return 0 ;
}
# else
int perf_event__synthesize_cgroups ( struct perf_tool * tool __maybe_unused ,
perf_event__handler_t process __maybe_unused ,
struct machine * machine __maybe_unused )
{
return - 1 ;
}
# endif
2019-09-18 22:08:52 +03:00
int perf_event__synthesize_modules ( struct perf_tool * tool , perf_event__handler_t process ,
struct machine * machine )
{
int rc = 0 ;
struct map * pos ;
struct maps * maps = machine__kernel_maps ( machine ) ;
2020-12-14 13:54:51 +03:00
union perf_event * event ;
size_t size = symbol_conf . buildid_mmap2 ?
sizeof ( event - > mmap2 ) : sizeof ( event - > mmap ) ;
event = zalloc ( size + machine - > id_hdr_size ) ;
2019-09-18 22:08:52 +03:00
if ( event = = NULL ) {
pr_debug ( " Not enough memory synthesizing mmap event "
" for kernel modules \n " ) ;
return - 1 ;
}
/*
* kernel uses 0 for user space maps , see kernel / perf_event . c
* __perf_event_mmap
*/
if ( machine__is_host ( machine ) )
event - > header . misc = PERF_RECORD_MISC_KERNEL ;
else
event - > header . misc = PERF_RECORD_MISC_GUEST_KERNEL ;
2019-10-28 17:31:38 +03:00
maps__for_each_entry ( maps , pos ) {
2019-09-18 22:08:52 +03:00
if ( ! __map__is_kmodule ( pos ) )
continue ;
2020-12-14 13:54:51 +03:00
if ( symbol_conf . buildid_mmap2 ) {
size = PERF_ALIGN ( pos - > dso - > long_name_len + 1 , sizeof ( u64 ) ) ;
event - > mmap2 . header . type = PERF_RECORD_MMAP2 ;
event - > mmap2 . header . size = ( sizeof ( event - > mmap2 ) -
( sizeof ( event - > mmap2 . filename ) - size ) ) ;
memset ( event - > mmap2 . filename + size , 0 , machine - > id_hdr_size ) ;
event - > mmap2 . header . size + = machine - > id_hdr_size ;
event - > mmap2 . start = pos - > start ;
event - > mmap2 . len = pos - > end - pos - > start ;
event - > mmap2 . pid = machine - > pid ;
memcpy ( event - > mmap2 . filename , pos - > dso - > long_name ,
pos - > dso - > long_name_len + 1 ) ;
2020-12-14 13:54:52 +03:00
perf_record_mmap2__read_build_id ( & event - > mmap2 , false ) ;
2020-12-14 13:54:51 +03:00
} else {
size = PERF_ALIGN ( pos - > dso - > long_name_len + 1 , sizeof ( u64 ) ) ;
event - > mmap . header . type = PERF_RECORD_MMAP ;
event - > mmap . header . size = ( sizeof ( event - > mmap ) -
( sizeof ( event - > mmap . filename ) - size ) ) ;
memset ( event - > mmap . filename + size , 0 , machine - > id_hdr_size ) ;
event - > mmap . header . size + = machine - > id_hdr_size ;
event - > mmap . start = pos - > start ;
event - > mmap . len = pos - > end - pos - > start ;
event - > mmap . pid = machine - > pid ;
memcpy ( event - > mmap . filename , pos - > dso - > long_name ,
pos - > dso - > long_name_len + 1 ) ;
}
2019-09-18 22:08:52 +03:00
if ( perf_tool__process_synth_event ( tool , event , machine , process ) ! = 0 ) {
rc = - 1 ;
break ;
}
}
free ( event ) ;
return rc ;
}
2021-02-02 12:01:18 +03:00
static int filter_task ( const struct dirent * dirent )
{
return isdigit ( dirent - > d_name [ 0 ] ) ;
}
2019-09-18 22:08:52 +03:00
static int __event__synthesize_thread ( union perf_event * comm_event ,
union perf_event * mmap_event ,
union perf_event * fork_event ,
union perf_event * namespaces_event ,
pid_t pid , int full , perf_event__handler_t process ,
2021-08-11 07:46:57 +03:00
struct perf_tool * tool , struct machine * machine ,
bool needs_mmap , bool mmap_data )
2019-09-18 22:08:52 +03:00
{
char filename [ PATH_MAX ] ;
2021-02-02 12:01:18 +03:00
struct dirent * * dirent ;
2019-09-18 22:08:52 +03:00
pid_t tgid , ppid ;
int rc = 0 ;
2021-02-02 12:01:18 +03:00
int i , n ;
2019-09-18 22:08:52 +03:00
/* special case: only send one comm event using passed in pid */
if ( ! full ) {
tgid = perf_event__synthesize_comm ( tool , comm_event , pid ,
process , machine ) ;
if ( tgid = = - 1 )
return - 1 ;
if ( perf_event__synthesize_namespaces ( tool , namespaces_event , pid ,
tgid , process , machine ) < 0 )
return - 1 ;
/*
* send mmap only for thread group leader
2019-11-26 03:58:33 +03:00
* see thread__init_maps ( )
2019-09-18 22:08:52 +03:00
*/
2021-08-11 07:46:57 +03:00
if ( pid = = tgid & & needs_mmap & &
2019-09-18 22:08:52 +03:00
perf_event__synthesize_mmap_events ( tool , mmap_event , pid , tgid ,
process , machine , mmap_data ) )
return - 1 ;
return 0 ;
}
if ( machine__is_default_guest ( machine ) )
return 0 ;
snprintf ( filename , sizeof ( filename ) , " %s/proc/%d/task " ,
machine - > root_dir , pid ) ;
2021-02-02 12:01:18 +03:00
n = scandir ( filename , & dirent , filter_task , alphasort ) ;
if ( n < 0 )
return n ;
2019-09-18 22:08:52 +03:00
2021-02-02 12:01:18 +03:00
for ( i = 0 ; i < n ; i + + ) {
2019-09-18 22:08:52 +03:00
char * end ;
pid_t _pid ;
2021-03-09 14:04:47 +03:00
bool kernel_thread = false ;
2019-09-18 22:08:52 +03:00
2021-02-02 12:01:18 +03:00
_pid = strtol ( dirent [ i ] - > d_name , & end , 10 ) ;
2019-09-18 22:08:52 +03:00
if ( * end )
continue ;
rc = - 1 ;
2021-02-02 12:01:16 +03:00
if ( perf_event__prepare_comm ( comm_event , pid , _pid , machine ,
2021-02-02 12:01:17 +03:00
& tgid , & ppid , & kernel_thread ) ! = 0 )
2019-09-18 22:08:52 +03:00
break ;
if ( perf_event__synthesize_fork ( tool , fork_event , _pid , tgid ,
ppid , process , machine ) < 0 )
break ;
if ( perf_event__synthesize_namespaces ( tool , namespaces_event , _pid ,
tgid , process , machine ) < 0 )
break ;
/*
* Send the prepared comm event
*/
if ( perf_tool__process_synth_event ( tool , comm_event , machine , process ) ! = 0 )
break ;
rc = 0 ;
2021-08-11 07:46:57 +03:00
if ( _pid = = pid & & ! kernel_thread & & needs_mmap ) {
2019-09-18 22:08:52 +03:00
/* process the parent's maps too */
rc = perf_event__synthesize_mmap_events ( tool , mmap_event , pid , tgid ,
process , machine , mmap_data ) ;
if ( rc )
break ;
}
}
2021-02-02 12:01:18 +03:00
for ( i = 0 ; i < n ; i + + )
zfree ( & dirent [ i ] ) ;
free ( dirent ) ;
2019-09-18 22:08:52 +03:00
return rc ;
}
int perf_event__synthesize_thread_map ( struct perf_tool * tool ,
struct perf_thread_map * threads ,
perf_event__handler_t process ,
struct machine * machine ,
2021-08-11 07:46:57 +03:00
bool needs_mmap , bool mmap_data )
2019-09-18 22:08:52 +03:00
{
union perf_event * comm_event , * mmap_event , * fork_event ;
union perf_event * namespaces_event ;
int err = - 1 , thread , j ;
comm_event = malloc ( sizeof ( comm_event - > comm ) + machine - > id_hdr_size ) ;
if ( comm_event = = NULL )
goto out ;
mmap_event = malloc ( sizeof ( mmap_event - > mmap2 ) + machine - > id_hdr_size ) ;
if ( mmap_event = = NULL )
goto out_free_comm ;
fork_event = malloc ( sizeof ( fork_event - > fork ) + machine - > id_hdr_size ) ;
if ( fork_event = = NULL )
goto out_free_mmap ;
namespaces_event = malloc ( sizeof ( namespaces_event - > namespaces ) +
( NR_NAMESPACES * sizeof ( struct perf_ns_link_info ) ) +
machine - > id_hdr_size ) ;
if ( namespaces_event = = NULL )
goto out_free_fork ;
err = 0 ;
for ( thread = 0 ; thread < threads - > nr ; + + thread ) {
if ( __event__synthesize_thread ( comm_event , mmap_event ,
fork_event , namespaces_event ,
perf_thread_map__pid ( threads , thread ) , 0 ,
process , tool , machine ,
2021-08-11 07:46:57 +03:00
needs_mmap , mmap_data ) ) {
2019-09-18 22:08:52 +03:00
err = - 1 ;
break ;
}
/*
* comm . pid is set to thread group id by
* perf_event__synthesize_comm
*/
if ( ( int ) comm_event - > comm . pid ! = perf_thread_map__pid ( threads , thread ) ) {
bool need_leader = true ;
/* is thread group leader in thread_map? */
for ( j = 0 ; j < threads - > nr ; + + j ) {
if ( ( int ) comm_event - > comm . pid = = perf_thread_map__pid ( threads , j ) ) {
need_leader = false ;
break ;
}
}
/* if not, generate events for it */
if ( need_leader & &
__event__synthesize_thread ( comm_event , mmap_event ,
fork_event , namespaces_event ,
comm_event - > comm . pid , 0 ,
process , tool , machine ,
2021-08-11 07:46:57 +03:00
needs_mmap , mmap_data ) ) {
2019-09-18 22:08:52 +03:00
err = - 1 ;
break ;
}
}
}
free ( namespaces_event ) ;
out_free_fork :
free ( fork_event ) ;
out_free_mmap :
free ( mmap_event ) ;
out_free_comm :
free ( comm_event ) ;
out :
return err ;
}
static int __perf_event__synthesize_threads ( struct perf_tool * tool ,
perf_event__handler_t process ,
struct machine * machine ,
2021-08-11 07:46:57 +03:00
bool needs_mmap ,
2019-09-18 22:08:52 +03:00
bool mmap_data ,
struct dirent * * dirent ,
int start ,
int num )
{
union perf_event * comm_event , * mmap_event , * fork_event ;
union perf_event * namespaces_event ;
int err = - 1 ;
char * end ;
pid_t pid ;
int i ;
comm_event = malloc ( sizeof ( comm_event - > comm ) + machine - > id_hdr_size ) ;
if ( comm_event = = NULL )
goto out ;
mmap_event = malloc ( sizeof ( mmap_event - > mmap2 ) + machine - > id_hdr_size ) ;
if ( mmap_event = = NULL )
goto out_free_comm ;
fork_event = malloc ( sizeof ( fork_event - > fork ) + machine - > id_hdr_size ) ;
if ( fork_event = = NULL )
goto out_free_mmap ;
namespaces_event = malloc ( sizeof ( namespaces_event - > namespaces ) +
( NR_NAMESPACES * sizeof ( struct perf_ns_link_info ) ) +
machine - > id_hdr_size ) ;
if ( namespaces_event = = NULL )
goto out_free_fork ;
for ( i = start ; i < start + num ; i + + ) {
if ( ! isdigit ( dirent [ i ] - > d_name [ 0 ] ) )
continue ;
pid = ( pid_t ) strtol ( dirent [ i ] - > d_name , & end , 10 ) ;
/* only interested in proper numerical dirents */
if ( * end )
continue ;
/*
* We may race with exiting thread , so don ' t stop just because
* one thread couldn ' t be synthesized .
*/
__event__synthesize_thread ( comm_event , mmap_event , fork_event ,
namespaces_event , pid , 1 , process ,
2021-08-11 07:46:57 +03:00
tool , machine , needs_mmap , mmap_data ) ;
2019-09-18 22:08:52 +03:00
}
err = 0 ;
free ( namespaces_event ) ;
out_free_fork :
free ( fork_event ) ;
out_free_mmap :
free ( mmap_event ) ;
out_free_comm :
free ( comm_event ) ;
out :
return err ;
}
struct synthesize_threads_arg {
struct perf_tool * tool ;
perf_event__handler_t process ;
struct machine * machine ;
2021-08-11 07:46:57 +03:00
bool needs_mmap ;
2019-09-18 22:08:52 +03:00
bool mmap_data ;
struct dirent * * dirent ;
int num ;
int start ;
} ;
static void * synthesize_threads_worker ( void * arg )
{
struct synthesize_threads_arg * args = arg ;
__perf_event__synthesize_threads ( args - > tool , args - > process ,
2021-08-11 07:46:57 +03:00
args - > machine ,
args - > needs_mmap , args - > mmap_data ,
2019-09-18 22:08:52 +03:00
args - > dirent ,
args - > start , args - > num ) ;
return NULL ;
}
int perf_event__synthesize_threads ( struct perf_tool * tool ,
perf_event__handler_t process ,
struct machine * machine ,
2021-08-11 07:46:57 +03:00
bool needs_mmap , bool mmap_data ,
2019-09-18 22:08:52 +03:00
unsigned int nr_threads_synthesize )
{
struct synthesize_threads_arg * args = NULL ;
pthread_t * synthesize_threads = NULL ;
char proc_path [ PATH_MAX ] ;
struct dirent * * dirent ;
int num_per_thread ;
int m , n , i , j ;
int thread_nr ;
int base = 0 ;
int err = - 1 ;
if ( machine__is_default_guest ( machine ) )
return 0 ;
snprintf ( proc_path , sizeof ( proc_path ) , " %s/proc " , machine - > root_dir ) ;
2021-02-02 12:01:18 +03:00
n = scandir ( proc_path , & dirent , filter_task , alphasort ) ;
2019-09-18 22:08:52 +03:00
if ( n < 0 )
return err ;
if ( nr_threads_synthesize = = UINT_MAX )
thread_nr = sysconf ( _SC_NPROCESSORS_ONLN ) ;
else
thread_nr = nr_threads_synthesize ;
if ( thread_nr < = 1 ) {
err = __perf_event__synthesize_threads ( tool , process ,
2021-08-11 07:46:57 +03:00
machine ,
needs_mmap , mmap_data ,
2019-09-18 22:08:52 +03:00
dirent , base , n ) ;
goto free_dirent ;
}
if ( thread_nr > n )
thread_nr = n ;
synthesize_threads = calloc ( sizeof ( pthread_t ) , thread_nr ) ;
if ( synthesize_threads = = NULL )
goto free_dirent ;
args = calloc ( sizeof ( * args ) , thread_nr ) ;
if ( args = = NULL )
goto free_threads ;
num_per_thread = n / thread_nr ;
m = n % thread_nr ;
for ( i = 0 ; i < thread_nr ; i + + ) {
args [ i ] . tool = tool ;
args [ i ] . process = process ;
args [ i ] . machine = machine ;
2021-08-11 07:46:57 +03:00
args [ i ] . needs_mmap = needs_mmap ;
2019-09-18 22:08:52 +03:00
args [ i ] . mmap_data = mmap_data ;
args [ i ] . dirent = dirent ;
}
for ( i = 0 ; i < m ; i + + ) {
args [ i ] . num = num_per_thread + 1 ;
args [ i ] . start = i * args [ i ] . num ;
}
if ( i ! = 0 )
base = args [ i - 1 ] . start + args [ i - 1 ] . num ;
for ( j = i ; j < thread_nr ; j + + ) {
args [ j ] . num = num_per_thread ;
args [ j ] . start = base + ( j - i ) * args [ i ] . num ;
}
for ( i = 0 ; i < thread_nr ; i + + ) {
if ( pthread_create ( & synthesize_threads [ i ] , NULL ,
synthesize_threads_worker , & args [ i ] ) )
goto out_join ;
}
err = 0 ;
out_join :
for ( i = 0 ; i < thread_nr ; i + + )
pthread_join ( synthesize_threads [ i ] , NULL ) ;
free ( args ) ;
free_threads :
free ( synthesize_threads ) ;
free_dirent :
for ( i = 0 ; i < n ; i + + )
zfree ( & dirent [ i ] ) ;
free ( dirent ) ;
return err ;
}
int __weak perf_event__synthesize_extra_kmaps ( struct perf_tool * tool __maybe_unused ,
perf_event__handler_t process __maybe_unused ,
struct machine * machine __maybe_unused )
{
return 0 ;
}
static int __perf_event__synthesize_kernel_mmap ( struct perf_tool * tool ,
perf_event__handler_t process ,
struct machine * machine )
{
2020-12-14 13:54:50 +03:00
union perf_event * event ;
size_t size = symbol_conf . buildid_mmap2 ?
sizeof ( event - > mmap2 ) : sizeof ( event - > mmap ) ;
2019-09-18 22:08:52 +03:00
struct map * map = machine__kernel_map ( machine ) ;
struct kmap * kmap ;
int err ;
if ( map = = NULL )
return - 1 ;
kmap = map__kmap ( map ) ;
if ( ! kmap - > ref_reloc_sym )
return - 1 ;
/*
* We should get this from / sys / kernel / sections / . text , but till that is
* available use this , and after it is use this as a fallback for older
* kernels .
*/
2020-12-14 13:54:50 +03:00
event = zalloc ( size + machine - > id_hdr_size ) ;
2019-09-18 22:08:52 +03:00
if ( event = = NULL ) {
pr_debug ( " Not enough memory synthesizing mmap event "
" for kernel modules \n " ) ;
return - 1 ;
}
if ( machine__is_host ( machine ) ) {
/*
* kernel uses PERF_RECORD_MISC_USER for user space maps ,
* see kernel / perf_event . c __perf_event_mmap
*/
event - > header . misc = PERF_RECORD_MISC_KERNEL ;
} else {
event - > header . misc = PERF_RECORD_MISC_GUEST_KERNEL ;
}
2020-12-14 13:54:50 +03:00
if ( symbol_conf . buildid_mmap2 ) {
size = snprintf ( event - > mmap2 . filename , sizeof ( event - > mmap2 . filename ) ,
" %s%s " , machine - > mmap_name , kmap - > ref_reloc_sym - > name ) + 1 ;
size = PERF_ALIGN ( size , sizeof ( u64 ) ) ;
event - > mmap2 . header . type = PERF_RECORD_MMAP2 ;
event - > mmap2 . header . size = ( sizeof ( event - > mmap2 ) -
( sizeof ( event - > mmap2 . filename ) - size ) + machine - > id_hdr_size ) ;
event - > mmap2 . pgoff = kmap - > ref_reloc_sym - > addr ;
event - > mmap2 . start = map - > start ;
event - > mmap2 . len = map - > end - event - > mmap . start ;
event - > mmap2 . pid = machine - > pid ;
2020-12-14 13:54:52 +03:00
perf_record_mmap2__read_build_id ( & event - > mmap2 , true ) ;
2020-12-14 13:54:50 +03:00
} else {
size = snprintf ( event - > mmap . filename , sizeof ( event - > mmap . filename ) ,
" %s%s " , machine - > mmap_name , kmap - > ref_reloc_sym - > name ) + 1 ;
size = PERF_ALIGN ( size , sizeof ( u64 ) ) ;
event - > mmap . header . type = PERF_RECORD_MMAP ;
event - > mmap . header . size = ( sizeof ( event - > mmap ) -
( sizeof ( event - > mmap . filename ) - size ) + machine - > id_hdr_size ) ;
event - > mmap . pgoff = kmap - > ref_reloc_sym - > addr ;
event - > mmap . start = map - > start ;
event - > mmap . len = map - > end - event - > mmap . start ;
event - > mmap . pid = machine - > pid ;
}
2019-09-18 22:08:52 +03:00
err = perf_tool__process_synth_event ( tool , event , machine , process ) ;
free ( event ) ;
return err ;
}
int perf_event__synthesize_kernel_mmap ( struct perf_tool * tool ,
perf_event__handler_t process ,
struct machine * machine )
{
int err ;
err = __perf_event__synthesize_kernel_mmap ( tool , process , machine ) ;
if ( err < 0 )
return err ;
return perf_event__synthesize_extra_kmaps ( tool , process , machine ) ;
}
int perf_event__synthesize_thread_map2 ( struct perf_tool * tool ,
struct perf_thread_map * threads ,
perf_event__handler_t process ,
struct machine * machine )
{
union perf_event * event ;
int i , err , size ;
size = sizeof ( event - > thread_map ) ;
size + = threads - > nr * sizeof ( event - > thread_map . entries [ 0 ] ) ;
event = zalloc ( size ) ;
if ( ! event )
return - ENOMEM ;
event - > header . type = PERF_RECORD_THREAD_MAP ;
event - > header . size = size ;
event - > thread_map . nr = threads - > nr ;
for ( i = 0 ; i < threads - > nr ; i + + ) {
struct perf_record_thread_map_entry * entry = & event - > thread_map . entries [ i ] ;
char * comm = perf_thread_map__comm ( threads , i ) ;
if ( ! comm )
comm = ( char * ) " " ;
entry - > pid = perf_thread_map__pid ( threads , i ) ;
strncpy ( ( char * ) & entry - > comm , comm , sizeof ( entry - > comm ) ) ;
}
err = process ( tool , event , NULL , machine ) ;
free ( event ) ;
return err ;
}
static void synthesize_cpus ( struct cpu_map_entries * cpus ,
struct perf_cpu_map * map )
{
int i ;
cpus - > nr = map - > nr ;
for ( i = 0 ; i < map - > nr ; i + + )
cpus - > cpu [ i ] = map - > map [ i ] ;
}
static void synthesize_mask ( struct perf_record_record_cpu_map * mask ,
struct perf_cpu_map * map , int max )
{
int i ;
mask - > nr = BITS_TO_LONGS ( max ) ;
mask - > long_size = sizeof ( long ) ;
for ( i = 0 ; i < map - > nr ; i + + )
set_bit ( map - > map [ i ] , mask - > mask ) ;
}
static size_t cpus_size ( struct perf_cpu_map * map )
{
return sizeof ( struct cpu_map_entries ) + map - > nr * sizeof ( u16 ) ;
}
static size_t mask_size ( struct perf_cpu_map * map , int * max )
{
int i ;
* max = 0 ;
for ( i = 0 ; i < map - > nr ; i + + ) {
2021-03-23 19:09:15 +03:00
/* bit position of the cpu is + 1 */
2019-09-18 22:08:52 +03:00
int bit = map - > map [ i ] + 1 ;
if ( bit > * max )
* max = bit ;
}
return sizeof ( struct perf_record_record_cpu_map ) + BITS_TO_LONGS ( * max ) * sizeof ( long ) ;
}
void * cpu_map_data__alloc ( struct perf_cpu_map * map , size_t * size , u16 * type , int * max )
{
size_t size_cpus , size_mask ;
bool is_dummy = perf_cpu_map__empty ( map ) ;
/*
* Both array and mask data have variable size based
* on the number of cpus and their actual values .
* The size of the ' struct perf_record_cpu_map_data ' is :
*
* array = size of ' struct cpu_map_entries ' +
* number of cpus * sizeof ( u64 )
*
* mask = size of ' struct perf_record_record_cpu_map ' +
* maximum cpu bit converted to size of longs
*
2021-03-23 19:09:15 +03:00
* and finally + the size of ' struct perf_record_cpu_map_data ' .
2019-09-18 22:08:52 +03:00
*/
size_cpus = cpus_size ( map ) ;
size_mask = mask_size ( map , max ) ;
if ( is_dummy | | ( size_cpus < size_mask ) ) {
* size + = size_cpus ;
* type = PERF_CPU_MAP__CPUS ;
} else {
* size + = size_mask ;
* type = PERF_CPU_MAP__MASK ;
}
* size + = sizeof ( struct perf_record_cpu_map_data ) ;
* size = PERF_ALIGN ( * size , sizeof ( u64 ) ) ;
return zalloc ( * size ) ;
}
void cpu_map_data__synthesize ( struct perf_record_cpu_map_data * data , struct perf_cpu_map * map ,
u16 type , int max )
{
data - > type = type ;
switch ( type ) {
case PERF_CPU_MAP__CPUS :
synthesize_cpus ( ( struct cpu_map_entries * ) data - > data , map ) ;
break ;
case PERF_CPU_MAP__MASK :
synthesize_mask ( ( struct perf_record_record_cpu_map * ) data - > data , map , max ) ;
default :
break ;
2020-04-28 12:18:43 +03:00
}
2019-09-18 22:08:52 +03:00
}
static struct perf_record_cpu_map * cpu_map_event__new ( struct perf_cpu_map * map )
{
size_t size = sizeof ( struct perf_record_cpu_map ) ;
struct perf_record_cpu_map * event ;
int max ;
u16 type ;
event = cpu_map_data__alloc ( map , & size , & type , & max ) ;
if ( ! event )
return NULL ;
event - > header . type = PERF_RECORD_CPU_MAP ;
event - > header . size = size ;
event - > data . type = type ;
cpu_map_data__synthesize ( & event - > data , map , type , max ) ;
return event ;
}
int perf_event__synthesize_cpu_map ( struct perf_tool * tool ,
struct perf_cpu_map * map ,
perf_event__handler_t process ,
struct machine * machine )
{
struct perf_record_cpu_map * event ;
int err ;
event = cpu_map_event__new ( map ) ;
if ( ! event )
return - ENOMEM ;
err = process ( tool , ( union perf_event * ) event , NULL , machine ) ;
free ( event ) ;
return err ;
}
int perf_event__synthesize_stat_config ( struct perf_tool * tool ,
struct perf_stat_config * config ,
perf_event__handler_t process ,
struct machine * machine )
{
struct perf_record_stat_config * event ;
int size , i = 0 , err ;
size = sizeof ( * event ) ;
size + = ( PERF_STAT_CONFIG_TERM__MAX * sizeof ( event - > data [ 0 ] ) ) ;
event = zalloc ( size ) ;
if ( ! event )
return - ENOMEM ;
event - > header . type = PERF_RECORD_STAT_CONFIG ;
event - > header . size = size ;
event - > nr = PERF_STAT_CONFIG_TERM__MAX ;
# define ADD(__term, __val) \
event - > data [ i ] . tag = PERF_STAT_CONFIG_TERM__ # # __term ; \
event - > data [ i ] . val = __val ; \
i + + ;
ADD ( AGGR_MODE , config - > aggr_mode )
ADD ( INTERVAL , config - > interval )
ADD ( SCALE , config - > scale )
WARN_ONCE ( i ! = PERF_STAT_CONFIG_TERM__MAX ,
" stat config terms unbalanced \n " ) ;
# undef ADD
err = process ( tool , ( union perf_event * ) event , NULL , machine ) ;
free ( event ) ;
return err ;
}
int perf_event__synthesize_stat ( struct perf_tool * tool ,
u32 cpu , u32 thread , u64 id ,
struct perf_counts_values * count ,
perf_event__handler_t process ,
struct machine * machine )
{
struct perf_record_stat event ;
event . header . type = PERF_RECORD_STAT ;
event . header . size = sizeof ( event ) ;
event . header . misc = 0 ;
event . id = id ;
event . cpu = cpu ;
event . thread = thread ;
event . val = count - > val ;
event . ena = count - > ena ;
event . run = count - > run ;
return process ( tool , ( union perf_event * ) & event , NULL , machine ) ;
}
int perf_event__synthesize_stat_round ( struct perf_tool * tool ,
u64 evtime , u64 type ,
perf_event__handler_t process ,
struct machine * machine )
{
struct perf_record_stat_round event ;
event . header . type = PERF_RECORD_STAT_ROUND ;
event . header . size = sizeof ( event ) ;
event . header . misc = 0 ;
event . time = evtime ;
event . type = type ;
return process ( tool , ( union perf_event * ) & event , NULL , machine ) ;
}
size_t perf_event__sample_event_size ( const struct perf_sample * sample , u64 type , u64 read_format )
{
size_t sz , result = sizeof ( struct perf_record_sample ) ;
if ( type & PERF_SAMPLE_IDENTIFIER )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_IP )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_TID )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_TIME )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_ADDR )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_ID )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_STREAM_ID )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_CPU )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_PERIOD )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_READ ) {
result + = sizeof ( u64 ) ;
if ( read_format & PERF_FORMAT_TOTAL_TIME_ENABLED )
result + = sizeof ( u64 ) ;
if ( read_format & PERF_FORMAT_TOTAL_TIME_RUNNING )
result + = sizeof ( u64 ) ;
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
if ( read_format & PERF_FORMAT_GROUP ) {
sz = sample - > read . group . nr *
sizeof ( struct sample_read_value ) ;
result + = sz ;
} else {
result + = sizeof ( u64 ) ;
}
}
if ( type & PERF_SAMPLE_CALLCHAIN ) {
sz = ( sample - > callchain - > nr + 1 ) * sizeof ( u64 ) ;
result + = sz ;
}
if ( type & PERF_SAMPLE_RAW ) {
result + = sizeof ( u32 ) ;
result + = sample - > raw_size ;
}
if ( type & PERF_SAMPLE_BRANCH_STACK ) {
sz = sample - > branch_stack - > nr * sizeof ( struct branch_entry ) ;
2020-02-28 19:30:00 +03:00
/* nr, hw_idx */
sz + = 2 * sizeof ( u64 ) ;
2019-09-18 22:08:52 +03:00
result + = sz ;
}
if ( type & PERF_SAMPLE_REGS_USER ) {
if ( sample - > user_regs . abi ) {
result + = sizeof ( u64 ) ;
sz = hweight64 ( sample - > user_regs . mask ) * sizeof ( u64 ) ;
result + = sz ;
} else {
result + = sizeof ( u64 ) ;
}
}
if ( type & PERF_SAMPLE_STACK_USER ) {
sz = sample - > user_stack . size ;
result + = sizeof ( u64 ) ;
if ( sz ) {
result + = sz ;
result + = sizeof ( u64 ) ;
}
}
2021-02-02 23:09:09 +03:00
if ( type & PERF_SAMPLE_WEIGHT_TYPE )
2019-09-18 22:08:52 +03:00
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_DATA_SRC )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_TRANSACTION )
result + = sizeof ( u64 ) ;
if ( type & PERF_SAMPLE_REGS_INTR ) {
if ( sample - > intr_regs . abi ) {
result + = sizeof ( u64 ) ;
sz = hweight64 ( sample - > intr_regs . mask ) * sizeof ( u64 ) ;
result + = sz ;
} else {
result + = sizeof ( u64 ) ;
}
}
if ( type & PERF_SAMPLE_PHYS_ADDR )
result + = sizeof ( u64 ) ;
2020-03-25 15:45:30 +03:00
if ( type & PERF_SAMPLE_CGROUP )
result + = sizeof ( u64 ) ;
2020-11-30 20:27:53 +03:00
if ( type & PERF_SAMPLE_DATA_PAGE_SIZE )
result + = sizeof ( u64 ) ;
2021-01-05 22:57:49 +03:00
if ( type & PERF_SAMPLE_CODE_PAGE_SIZE )
result + = sizeof ( u64 ) ;
2019-11-15 15:42:11 +03:00
if ( type & PERF_SAMPLE_AUX ) {
result + = sizeof ( u64 ) ;
result + = sample - > aux_sample . size ;
}
2019-09-18 22:08:52 +03:00
return result ;
}
2021-02-05 19:01:52 +03:00
void __weak arch_perf_synthesize_sample_weight ( const struct perf_sample * data ,
__u64 * array , u64 type __maybe_unused )
{
* array = data - > weight ;
}
2019-09-18 22:08:52 +03:00
int perf_event__synthesize_sample ( union perf_event * event , u64 type , u64 read_format ,
const struct perf_sample * sample )
{
__u64 * array ;
size_t sz ;
/*
* used for cross - endian analysis . See git commit 65014 ab3
* for why this goofiness is needed .
*/
union u64_swap u ;
array = event - > sample . array ;
if ( type & PERF_SAMPLE_IDENTIFIER ) {
* array = sample - > id ;
array + + ;
}
if ( type & PERF_SAMPLE_IP ) {
* array = sample - > ip ;
array + + ;
}
if ( type & PERF_SAMPLE_TID ) {
u . val32 [ 0 ] = sample - > pid ;
u . val32 [ 1 ] = sample - > tid ;
* array = u . val64 ;
array + + ;
}
if ( type & PERF_SAMPLE_TIME ) {
* array = sample - > time ;
array + + ;
}
if ( type & PERF_SAMPLE_ADDR ) {
* array = sample - > addr ;
array + + ;
}
if ( type & PERF_SAMPLE_ID ) {
* array = sample - > id ;
array + + ;
}
if ( type & PERF_SAMPLE_STREAM_ID ) {
* array = sample - > stream_id ;
array + + ;
}
if ( type & PERF_SAMPLE_CPU ) {
u . val32 [ 0 ] = sample - > cpu ;
u . val32 [ 1 ] = 0 ;
* array = u . val64 ;
array + + ;
}
if ( type & PERF_SAMPLE_PERIOD ) {
* array = sample - > period ;
array + + ;
}
if ( type & PERF_SAMPLE_READ ) {
if ( read_format & PERF_FORMAT_GROUP )
* array = sample - > read . group . nr ;
else
* array = sample - > read . one . value ;
array + + ;
if ( read_format & PERF_FORMAT_TOTAL_TIME_ENABLED ) {
* array = sample - > read . time_enabled ;
array + + ;
}
if ( read_format & PERF_FORMAT_TOTAL_TIME_RUNNING ) {
* array = sample - > read . time_running ;
array + + ;
}
/* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
if ( read_format & PERF_FORMAT_GROUP ) {
sz = sample - > read . group . nr *
sizeof ( struct sample_read_value ) ;
memcpy ( array , sample - > read . group . values , sz ) ;
array = ( void * ) array + sz ;
} else {
* array = sample - > read . one . id ;
array + + ;
}
}
if ( type & PERF_SAMPLE_CALLCHAIN ) {
sz = ( sample - > callchain - > nr + 1 ) * sizeof ( u64 ) ;
memcpy ( array , sample - > callchain , sz ) ;
array = ( void * ) array + sz ;
}
if ( type & PERF_SAMPLE_RAW ) {
u . val32 [ 0 ] = sample - > raw_size ;
* array = u . val64 ;
array = ( void * ) array + sizeof ( u32 ) ;
memcpy ( array , sample - > raw_data , sample - > raw_size ) ;
array = ( void * ) array + sample - > raw_size ;
}
if ( type & PERF_SAMPLE_BRANCH_STACK ) {
sz = sample - > branch_stack - > nr * sizeof ( struct branch_entry ) ;
2020-02-28 19:30:00 +03:00
/* nr, hw_idx */
sz + = 2 * sizeof ( u64 ) ;
2019-09-18 22:08:52 +03:00
memcpy ( array , sample - > branch_stack , sz ) ;
array = ( void * ) array + sz ;
}
if ( type & PERF_SAMPLE_REGS_USER ) {
if ( sample - > user_regs . abi ) {
* array + + = sample - > user_regs . abi ;
sz = hweight64 ( sample - > user_regs . mask ) * sizeof ( u64 ) ;
memcpy ( array , sample - > user_regs . regs , sz ) ;
array = ( void * ) array + sz ;
} else {
* array + + = 0 ;
}
}
if ( type & PERF_SAMPLE_STACK_USER ) {
sz = sample - > user_stack . size ;
* array + + = sz ;
if ( sz ) {
memcpy ( array , sample - > user_stack . data , sz ) ;
array = ( void * ) array + sz ;
* array + + = sz ;
}
}
2021-02-02 23:09:09 +03:00
if ( type & PERF_SAMPLE_WEIGHT_TYPE ) {
2021-02-05 19:01:52 +03:00
arch_perf_synthesize_sample_weight ( sample , array , type ) ;
2019-09-18 22:08:52 +03:00
array + + ;
}
if ( type & PERF_SAMPLE_DATA_SRC ) {
* array = sample - > data_src ;
array + + ;
}
if ( type & PERF_SAMPLE_TRANSACTION ) {
* array = sample - > transaction ;
array + + ;
}
if ( type & PERF_SAMPLE_REGS_INTR ) {
if ( sample - > intr_regs . abi ) {
* array + + = sample - > intr_regs . abi ;
sz = hweight64 ( sample - > intr_regs . mask ) * sizeof ( u64 ) ;
memcpy ( array , sample - > intr_regs . regs , sz ) ;
array = ( void * ) array + sz ;
} else {
* array + + = 0 ;
}
}
if ( type & PERF_SAMPLE_PHYS_ADDR ) {
* array = sample - > phys_addr ;
array + + ;
2020-03-25 15:45:30 +03:00
}
if ( type & PERF_SAMPLE_CGROUP ) {
* array = sample - > cgroup ;
array + + ;
2019-09-18 22:08:52 +03:00
}
2020-11-30 20:27:53 +03:00
if ( type & PERF_SAMPLE_DATA_PAGE_SIZE ) {
* array = sample - > data_page_size ;
array + + ;
}
2021-01-05 22:57:49 +03:00
if ( type & PERF_SAMPLE_CODE_PAGE_SIZE ) {
* array = sample - > code_page_size ;
array + + ;
}
2019-11-15 15:42:11 +03:00
if ( type & PERF_SAMPLE_AUX ) {
sz = sample - > aux_sample . size ;
* array + + = sz ;
memcpy ( array , sample - > aux_sample . data , sz ) ;
array = ( void * ) array + sz ;
}
2019-09-18 22:08:52 +03:00
return 0 ;
}
int perf_event__synthesize_id_index ( struct perf_tool * tool , perf_event__handler_t process ,
struct evlist * evlist , struct machine * machine )
{
union perf_event * ev ;
struct evsel * evsel ;
size_t nr = 0 , i = 0 , sz , max_nr , n ;
int err ;
pr_debug2 ( " Synthesizing id index \n " ) ;
max_nr = ( UINT16_MAX - sizeof ( struct perf_record_id_index ) ) /
sizeof ( struct id_index_entry ) ;
evlist__for_each_entry ( evlist , evsel )
2019-09-02 23:15:47 +03:00
nr + = evsel - > core . ids ;
2019-09-18 22:08:52 +03:00
n = nr > max_nr ? max_nr : nr ;
sz = sizeof ( struct perf_record_id_index ) + n * sizeof ( struct id_index_entry ) ;
ev = zalloc ( sz ) ;
if ( ! ev )
return - ENOMEM ;
ev - > id_index . header . type = PERF_RECORD_ID_INDEX ;
ev - > id_index . header . size = sz ;
ev - > id_index . nr = n ;
evlist__for_each_entry ( evlist , evsel ) {
u32 j ;
2019-09-02 23:15:47 +03:00
for ( j = 0 ; j < evsel - > core . ids ; j + + ) {
2019-09-18 22:08:52 +03:00
struct id_index_entry * e ;
struct perf_sample_id * sid ;
if ( i > = n ) {
err = process ( tool , ev , NULL , machine ) ;
if ( err )
goto out_err ;
nr - = n ;
i = 0 ;
}
e = & ev - > id_index . entries [ i + + ] ;
2019-09-02 23:12:26 +03:00
e - > id = evsel - > core . id [ j ] ;
2019-09-18 22:08:52 +03:00
2020-11-30 20:17:57 +03:00
sid = evlist__id2sid ( evlist , e - > id ) ;
2019-09-18 22:08:52 +03:00
if ( ! sid ) {
free ( ev ) ;
return - ENOENT ;
}
e - > idx = sid - > idx ;
e - > cpu = sid - > cpu ;
e - > tid = sid - > tid ;
}
}
sz = sizeof ( struct perf_record_id_index ) + nr * sizeof ( struct id_index_entry ) ;
ev - > id_index . header . size = sz ;
ev - > id_index . nr = nr ;
err = process ( tool , ev , NULL , machine ) ;
out_err :
free ( ev ) ;
return err ;
}
int __machine__synthesize_threads ( struct machine * machine , struct perf_tool * tool ,
struct target * target , struct perf_thread_map * threads ,
2021-08-11 07:46:57 +03:00
perf_event__handler_t process , bool needs_mmap ,
bool data_mmap , unsigned int nr_threads_synthesize )
2019-09-18 22:08:52 +03:00
{
if ( target__has_task ( target ) )
2021-08-11 07:46:57 +03:00
return perf_event__synthesize_thread_map ( tool , threads , process , machine ,
needs_mmap , data_mmap ) ;
2019-09-18 22:08:52 +03:00
else if ( target__has_cpu ( target ) )
2021-08-11 07:46:57 +03:00
return perf_event__synthesize_threads ( tool , process , machine ,
needs_mmap , data_mmap ,
2019-09-18 22:08:52 +03:00
nr_threads_synthesize ) ;
/* command specified */
return 0 ;
}
int machine__synthesize_threads ( struct machine * machine , struct target * target ,
2021-08-11 07:46:57 +03:00
struct perf_thread_map * threads , bool needs_mmap ,
bool data_mmap , unsigned int nr_threads_synthesize )
2019-09-18 22:08:52 +03:00
{
return __machine__synthesize_threads ( machine , NULL , target , threads ,
2021-08-11 07:46:57 +03:00
perf_event__process , needs_mmap ,
data_mmap , nr_threads_synthesize ) ;
2019-09-18 22:08:52 +03:00
}
static struct perf_record_event_update * event_update_event__new ( size_t size , u64 type , u64 id )
{
struct perf_record_event_update * ev ;
size + = sizeof ( * ev ) ;
size = PERF_ALIGN ( size , sizeof ( u64 ) ) ;
ev = zalloc ( size ) ;
if ( ev ) {
ev - > header . type = PERF_RECORD_EVENT_UPDATE ;
ev - > header . size = ( u16 ) size ;
ev - > type = type ;
ev - > id = id ;
}
return ev ;
}
int perf_event__synthesize_event_update_unit ( struct perf_tool * tool , struct evsel * evsel ,
perf_event__handler_t process )
{
size_t size = strlen ( evsel - > unit ) ;
struct perf_record_event_update * ev ;
int err ;
2019-09-02 23:12:26 +03:00
ev = event_update_event__new ( size + 1 , PERF_EVENT_UPDATE__UNIT , evsel - > core . id [ 0 ] ) ;
2019-09-18 22:08:52 +03:00
if ( ev = = NULL )
return - ENOMEM ;
strlcpy ( ev - > data , evsel - > unit , size + 1 ) ;
err = process ( tool , ( union perf_event * ) ev , NULL , NULL ) ;
free ( ev ) ;
return err ;
}
int perf_event__synthesize_event_update_scale ( struct perf_tool * tool , struct evsel * evsel ,
perf_event__handler_t process )
{
struct perf_record_event_update * ev ;
struct perf_record_event_update_scale * ev_data ;
int err ;
2019-09-02 23:12:26 +03:00
ev = event_update_event__new ( sizeof ( * ev_data ) , PERF_EVENT_UPDATE__SCALE , evsel - > core . id [ 0 ] ) ;
2019-09-18 22:08:52 +03:00
if ( ev = = NULL )
return - ENOMEM ;
ev_data = ( struct perf_record_event_update_scale * ) ev - > data ;
ev_data - > scale = evsel - > scale ;
err = process ( tool , ( union perf_event * ) ev , NULL , NULL ) ;
free ( ev ) ;
return err ;
}
int perf_event__synthesize_event_update_name ( struct perf_tool * tool , struct evsel * evsel ,
perf_event__handler_t process )
{
struct perf_record_event_update * ev ;
size_t len = strlen ( evsel - > name ) ;
int err ;
2019-09-02 23:12:26 +03:00
ev = event_update_event__new ( len + 1 , PERF_EVENT_UPDATE__NAME , evsel - > core . id [ 0 ] ) ;
2019-09-18 22:08:52 +03:00
if ( ev = = NULL )
return - ENOMEM ;
strlcpy ( ev - > data , evsel - > name , len + 1 ) ;
err = process ( tool , ( union perf_event * ) ev , NULL , NULL ) ;
free ( ev ) ;
return err ;
}
int perf_event__synthesize_event_update_cpus ( struct perf_tool * tool , struct evsel * evsel ,
perf_event__handler_t process )
{
size_t size = sizeof ( struct perf_record_event_update ) ;
struct perf_record_event_update * ev ;
int max , err ;
u16 type ;
if ( ! evsel - > core . own_cpus )
return 0 ;
ev = cpu_map_data__alloc ( evsel - > core . own_cpus , & size , & type , & max ) ;
if ( ! ev )
return - ENOMEM ;
ev - > header . type = PERF_RECORD_EVENT_UPDATE ;
ev - > header . size = ( u16 ) size ;
ev - > type = PERF_EVENT_UPDATE__CPUS ;
2019-09-02 23:12:26 +03:00
ev - > id = evsel - > core . id [ 0 ] ;
2019-09-18 22:08:52 +03:00
cpu_map_data__synthesize ( ( struct perf_record_cpu_map_data * ) ev - > data ,
evsel - > core . own_cpus , type , max ) ;
err = process ( tool , ( union perf_event * ) ev , NULL , NULL ) ;
free ( ev ) ;
return err ;
}
int perf_event__synthesize_attrs ( struct perf_tool * tool , struct evlist * evlist ,
perf_event__handler_t process )
{
struct evsel * evsel ;
int err = 0 ;
evlist__for_each_entry ( evlist , evsel ) {
2019-09-02 23:15:47 +03:00
err = perf_event__synthesize_attr ( tool , & evsel - > core . attr , evsel - > core . ids ,
2019-09-02 23:12:26 +03:00
evsel - > core . id , process ) ;
2019-09-18 22:08:52 +03:00
if ( err ) {
pr_debug ( " failed to create perf header attribute \n " ) ;
return err ;
}
}
return err ;
}
static bool has_unit ( struct evsel * evsel )
{
return evsel - > unit & & * evsel - > unit ;
}
static bool has_scale ( struct evsel * evsel )
{
return evsel - > scale ! = 1 ;
}
int perf_event__synthesize_extra_attr ( struct perf_tool * tool , struct evlist * evsel_list ,
perf_event__handler_t process , bool is_pipe )
{
struct evsel * evsel ;
int err ;
/*
* Synthesize other events stuff not carried within
* attr event - unit , scale , name
*/
evlist__for_each_entry ( evsel_list , evsel ) {
if ( ! evsel - > supported )
continue ;
/*
* Synthesize unit and scale only if it ' s defined .
*/
if ( has_unit ( evsel ) ) {
err = perf_event__synthesize_event_update_unit ( tool , evsel , process ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize evsel unit. \n " ) ;
return err ;
}
}
if ( has_scale ( evsel ) ) {
err = perf_event__synthesize_event_update_scale ( tool , evsel , process ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize evsel evsel. \n " ) ;
return err ;
}
}
if ( evsel - > core . own_cpus ) {
err = perf_event__synthesize_event_update_cpus ( tool , evsel , process ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize evsel cpus. \n " ) ;
return err ;
}
}
/*
* Name is needed only for pipe output ,
* perf . data carries event names .
*/
if ( is_pipe ) {
err = perf_event__synthesize_event_update_name ( tool , evsel , process ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize evsel name. \n " ) ;
return err ;
}
}
}
return 0 ;
}
int perf_event__synthesize_attr ( struct perf_tool * tool , struct perf_event_attr * attr ,
u32 ids , u64 * id , perf_event__handler_t process )
{
union perf_event * ev ;
size_t size ;
int err ;
size = sizeof ( struct perf_event_attr ) ;
size = PERF_ALIGN ( size , sizeof ( u64 ) ) ;
size + = sizeof ( struct perf_event_header ) ;
size + = ids * sizeof ( u64 ) ;
ev = zalloc ( size ) ;
if ( ev = = NULL )
return - ENOMEM ;
ev - > attr . attr = * attr ;
memcpy ( ev - > attr . id , id , ids * sizeof ( u64 ) ) ;
ev - > attr . header . type = PERF_RECORD_HEADER_ATTR ;
ev - > attr . header . size = ( u16 ) size ;
if ( ev - > attr . header . size = = size )
err = process ( tool , ev , NULL , NULL ) ;
else
err = - E2BIG ;
free ( ev ) ;
return err ;
}
int perf_event__synthesize_tracing_data ( struct perf_tool * tool , int fd , struct evlist * evlist ,
perf_event__handler_t process )
{
union perf_event ev ;
struct tracing_data * tdata ;
ssize_t size = 0 , aligned_size = 0 , padding ;
struct feat_fd ff ;
/*
* We are going to store the size of the data followed
* by the data contents . Since the fd descriptor is a pipe ,
* we cannot seek back to store the size of the data once
* we know it . Instead we :
*
* - write the tracing data to the temp file
* - get / write the data size to pipe
* - write the tracing data from the temp file
* to the pipe
*/
tdata = tracing_data_get ( & evlist - > core . entries , fd , true ) ;
if ( ! tdata )
return - 1 ;
memset ( & ev , 0 , sizeof ( ev ) ) ;
ev . tracing_data . header . type = PERF_RECORD_HEADER_TRACING_DATA ;
size = tdata - > size ;
aligned_size = PERF_ALIGN ( size , sizeof ( u64 ) ) ;
padding = aligned_size - size ;
ev . tracing_data . header . size = sizeof ( ev . tracing_data ) ;
ev . tracing_data . size = aligned_size ;
process ( tool , & ev , NULL , NULL ) ;
/*
* The put function will copy all the tracing data
* stored in temp file to the pipe .
*/
tracing_data_put ( tdata ) ;
ff = ( struct feat_fd ) { . fd = fd } ;
if ( write_padded ( & ff , NULL , 0 , padding ) )
return - 1 ;
return aligned_size ;
}
int perf_event__synthesize_build_id ( struct perf_tool * tool , struct dso * pos , u16 misc ,
perf_event__handler_t process , struct machine * machine )
{
union perf_event ev ;
size_t len ;
if ( ! pos - > hit )
return 0 ;
memset ( & ev , 0 , sizeof ( ev ) ) ;
len = pos - > long_name_len + 1 ;
len = PERF_ALIGN ( len , NAME_ALIGN ) ;
2020-10-13 22:24:33 +03:00
memcpy ( & ev . build_id . build_id , pos - > bid . data , sizeof ( pos - > bid . data ) ) ;
2019-09-18 22:08:52 +03:00
ev . build_id . header . type = PERF_RECORD_HEADER_BUILD_ID ;
ev . build_id . header . misc = misc ;
ev . build_id . pid = machine - > pid ;
ev . build_id . header . size = sizeof ( ev . build_id ) + len ;
memcpy ( & ev . build_id . filename , pos - > long_name , pos - > long_name_len ) ;
return process ( tool , & ev , NULL , machine ) ;
}
int perf_event__synthesize_stat_events ( struct perf_stat_config * config , struct perf_tool * tool ,
struct evlist * evlist , perf_event__handler_t process , bool attrs )
{
int err ;
if ( attrs ) {
err = perf_event__synthesize_attrs ( tool , evlist , process ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize attrs. \n " ) ;
return err ;
}
}
err = perf_event__synthesize_extra_attr ( tool , evlist , process , attrs ) ;
err = perf_event__synthesize_thread_map2 ( tool , evlist - > core . threads , process , NULL ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize thread map. \n " ) ;
return err ;
}
err = perf_event__synthesize_cpu_map ( tool , evlist - > core . cpus , process , NULL ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize thread map. \n " ) ;
return err ;
}
err = perf_event__synthesize_stat_config ( tool , config , process , NULL ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize config. \n " ) ;
return err ;
}
return 0 ;
}
extern const struct perf_header_feature_ops feat_ops [ HEADER_LAST_FEATURE ] ;
int perf_event__synthesize_features ( struct perf_tool * tool , struct perf_session * session ,
struct evlist * evlist , perf_event__handler_t process )
{
struct perf_header * header = & session - > header ;
struct perf_record_header_feature * fe ;
struct feat_fd ff ;
size_t sz , sz_hdr ;
int feat , ret ;
sz_hdr = sizeof ( fe - > header ) ;
sz = sizeof ( union perf_event ) ;
/* get a nice alignment */
sz = PERF_ALIGN ( sz , page_size ) ;
memset ( & ff , 0 , sizeof ( ff ) ) ;
ff . buf = malloc ( sz ) ;
if ( ! ff . buf )
return - ENOMEM ;
ff . size = sz - sz_hdr ;
ff . ph = & session - > header ;
for_each_set_bit ( feat , header - > adds_features , HEADER_FEAT_BITS ) {
if ( ! feat_ops [ feat ] . synthesize ) {
pr_debug ( " No record header feature for header :%d \n " , feat ) ;
continue ;
}
ff . offset = sizeof ( * fe ) ;
ret = feat_ops [ feat ] . write ( & ff , evlist ) ;
if ( ret | | ff . offset < = ( ssize_t ) sizeof ( * fe ) ) {
pr_debug ( " Error writing feature \n " ) ;
continue ;
}
/* ff.buf may have changed due to realloc in do_write() */
fe = ff . buf ;
memset ( fe , 0 , sizeof ( * fe ) ) ;
fe - > feat_id = feat ;
fe - > header . type = PERF_RECORD_HEADER_FEATURE ;
fe - > header . size = ff . offset ;
ret = process ( tool , ff . buf , NULL , NULL ) ;
if ( ret ) {
free ( ff . buf ) ;
return ret ;
}
}
/* Send HEADER_LAST_FEATURE mark. */
fe = ff . buf ;
fe - > feat_id = HEADER_LAST_FEATURE ;
fe - > header . type = PERF_RECORD_HEADER_FEATURE ;
fe - > header . size = sizeof ( * fe ) ;
ret = process ( tool , ff . buf , NULL , NULL ) ;
free ( ff . buf ) ;
return ret ;
}
2021-07-20 01:31:52 +03:00
int perf_event__synthesize_for_pipe ( struct perf_tool * tool ,
struct perf_session * session ,
struct perf_data * data ,
perf_event__handler_t process )
{
int err ;
int ret = 0 ;
struct evlist * evlist = session - > evlist ;
/*
* We need to synthesize events first , because some
* features works on top of them ( on report side ) .
*/
err = perf_event__synthesize_attrs ( tool , evlist , process ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize attrs. \n " ) ;
return err ;
}
ret + = err ;
err = perf_event__synthesize_features ( tool , session , evlist , process ) ;
if ( err < 0 ) {
pr_err ( " Couldn't synthesize features. \n " ) ;
return err ;
}
ret + = err ;
if ( have_tracepoints ( & evlist - > core . entries ) ) {
int fd = perf_data__fd ( data ) ;
/*
* FIXME err < = 0 here actually means that
* there were no tracepoints so its not really
* an error , just that we don ' t need to
* synthesize anything . We really have to
* return this more properly and also
* propagate errors that now are calling die ( )
*/
err = perf_event__synthesize_tracing_data ( tool , fd , evlist ,
process ) ;
if ( err < = 0 ) {
pr_err ( " Couldn't record tracing data. \n " ) ;
return err ;
}
ret + = err ;
}
return ret ;
}
2021-08-11 07:46:58 +03:00
int parse_synth_opt ( char * synth )
{
char * p , * q ;
int ret = 0 ;
if ( synth = = NULL )
return - 1 ;
for ( q = synth ; ( p = strsep ( & q , " , " ) ) ; p = q ) {
if ( ! strcasecmp ( p , " no " ) | | ! strcasecmp ( p , " none " ) )
return 0 ;
if ( ! strcasecmp ( p , " all " ) )
return PERF_SYNTH_ALL ;
if ( ! strcasecmp ( p , " task " ) )
ret | = PERF_SYNTH_TASK ;
else if ( ! strcasecmp ( p , " mmap " ) )
ret | = PERF_SYNTH_TASK | PERF_SYNTH_MMAP ;
else if ( ! strcasecmp ( p , " cgroup " ) )
ret | = PERF_SYNTH_CGROUP ;
else
return - 1 ;
}
return ret ;
}