2009-06-03 00:59:57 +04:00
/*
2009-06-03 01:37:05 +04:00
* builtin - record . c
*
* Builtin record command : Record the profile of a workload
* ( or a CPU , or a PID ) into the perf . data output file - for
* later analysis via perf report .
2009-06-03 00:59:57 +04:00
*/
2009-05-27 11:10:38 +04:00
# include "builtin.h"
2009-06-03 01:37:05 +04:00
# include "perf.h"
2009-05-01 20:29:57 +04:00
# include "util/util.h"
2009-05-26 11:17:18 +04:00
# include "util/parse-options.h"
2009-05-26 13:10:09 +04:00
# include "util/parse-events.h"
2009-06-02 00:50:19 +04:00
# include "util/string.h"
2009-05-01 20:29:57 +04:00
2009-06-02 17:52:24 +04:00
# include <unistd.h>
2009-04-08 17:01:31 +04:00
# include <sched.h>
2009-05-26 11:17:18 +04:00
# define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1)
# define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))
2009-05-15 05:50:46 +04:00
2009-04-08 17:01:31 +04:00
static int fd [ MAX_NR_CPUS ] [ MAX_COUNTERS ] ;
2009-06-06 11:58:57 +04:00
static long default_interval = 100000 ;
2009-06-03 01:02:59 +04:00
static int nr_cpus = 0 ;
2009-04-08 17:01:31 +04:00
static unsigned int page_size ;
2009-06-03 01:02:59 +04:00
static unsigned int mmap_pages = 128 ;
2009-06-05 15:27:02 +04:00
static int freq = 0 ;
2009-04-08 17:01:31 +04:00
static int output ;
2009-05-27 11:33:18 +04:00
static const char * output_name = " perf.data " ;
2009-04-08 17:01:31 +04:00
static int group = 0 ;
2009-05-05 19:50:27 +04:00
static unsigned int realtime_prio = 0 ;
static int system_wide = 0 ;
2009-05-15 05:50:46 +04:00
static pid_t target_pid = - 1 ;
2009-05-05 19:50:27 +04:00
static int inherit = 1 ;
2009-06-02 17:52:24 +04:00
static int force = 0 ;
2009-06-03 00:59:57 +04:00
static int append_file = 0 ;
2009-06-07 19:39:02 +04:00
static int verbose = 0 ;
2009-04-08 17:01:31 +04:00
2009-06-06 11:58:57 +04:00
static long samples ;
static struct timeval last_read ;
static struct timeval this_read ;
static __u64 bytes_written ;
static struct pollfd event_array [ MAX_NR_CPUS * MAX_COUNTERS ] ;
static int nr_poll ;
static int nr_cpu ;
struct mmap_event {
struct perf_event_header header ;
__u32 pid ;
__u32 tid ;
__u64 start ;
__u64 len ;
__u64 pgoff ;
char filename [ PATH_MAX ] ;
} ;
struct comm_event {
struct perf_event_header header ;
__u32 pid ;
__u32 tid ;
char comm [ 16 ] ;
2009-04-08 17:01:31 +04:00
} ;
2009-06-06 11:58:57 +04:00
2009-04-08 17:01:31 +04:00
struct mmap_data {
2009-06-06 11:58:57 +04:00
int counter ;
void * base ;
unsigned int mask ;
unsigned int prev ;
2009-04-08 17:01:31 +04:00
} ;
2009-06-06 11:58:57 +04:00
static struct mmap_data mmap_array [ MAX_NR_CPUS ] [ MAX_COUNTERS ] ;
2009-04-08 17:01:31 +04:00
static unsigned int mmap_read_head ( struct mmap_data * md )
{
struct perf_counter_mmap_page * pc = md - > base ;
int head ;
head = pc - > data_head ;
rmb ( ) ;
return head ;
}
static void mmap_read ( struct mmap_data * md )
{
unsigned int head = mmap_read_head ( md ) ;
unsigned int old = md - > prev ;
unsigned char * data = md - > base + page_size ;
unsigned long size ;
void * buf ;
int diff ;
gettimeofday ( & this_read , NULL ) ;
/*
* If we ' re further behind than half the buffer , there ' s a chance
2009-06-05 16:29:10 +04:00
* the writer will bite our tail and mess up the samples under us .
2009-04-08 17:01:31 +04:00
*
* If we somehow ended up ahead of the head , we got messed up .
*
* In either case , truncate and restart at head .
*/
diff = head - old ;
if ( diff > md - > mask / 2 | | diff < 0 ) {
struct timeval iv ;
unsigned long msecs ;
timersub ( & this_read , & last_read , & iv ) ;
msecs = iv . tv_sec * 1000 + iv . tv_usec / 1000 ;
fprintf ( stderr , " WARNING: failed to keep up with mmap data. "
" Last read %lu msecs ago. \n " , msecs ) ;
/*
* head points to a known good entry , start there .
*/
old = head ;
}
last_read = this_read ;
if ( old ! = head )
2009-06-05 16:29:10 +04:00
samples + + ;
2009-04-08 17:01:31 +04:00
size = head - old ;
if ( ( old & md - > mask ) + size ! = ( head & md - > mask ) ) {
buf = & data [ old & md - > mask ] ;
size = md - > mask + 1 - ( old & md - > mask ) ;
old + = size ;
2009-06-03 21:27:19 +04:00
2009-04-08 17:01:31 +04:00
while ( size ) {
int ret = write ( output , buf , size ) ;
2009-06-03 21:27:19 +04:00
if ( ret < 0 )
die ( " failed to write " ) ;
2009-04-08 17:01:31 +04:00
size - = ret ;
buf + = ret ;
2009-06-03 21:27:19 +04:00
bytes_written + = ret ;
2009-04-08 17:01:31 +04:00
}
}
buf = & data [ old & md - > mask ] ;
size = head - old ;
old + = size ;
2009-06-03 21:27:19 +04:00
2009-04-08 17:01:31 +04:00
while ( size ) {
int ret = write ( output , buf , size ) ;
2009-06-03 21:27:19 +04:00
if ( ret < 0 )
die ( " failed to write " ) ;
2009-04-08 17:01:31 +04:00
size - = ret ;
buf + = ret ;
2009-06-03 21:27:19 +04:00
bytes_written + = ret ;
2009-04-08 17:01:31 +04:00
}
md - > prev = old ;
}
static volatile int done = 0 ;
2009-06-10 17:55:59 +04:00
static volatile int signr = - 1 ;
2009-04-08 17:01:31 +04:00
2009-05-05 19:50:27 +04:00
static void sig_handler ( int sig )
2009-04-08 17:01:31 +04:00
{
2009-05-05 19:50:27 +04:00
done = 1 ;
2009-06-10 17:55:59 +04:00
signr = sig ;
}
static void sig_atexit ( void )
{
if ( signr = = - 1 )
return ;
signal ( signr , SIG_DFL ) ;
kill ( getpid ( ) , signr ) ;
2009-04-08 17:01:31 +04:00
}
2009-06-02 16:13:24 +04:00
static void pid_synthesize_comm_event ( pid_t pid , int full )
2009-05-15 05:50:46 +04:00
{
2009-05-27 11:10:38 +04:00
struct comm_event comm_ev ;
2009-05-15 05:50:46 +04:00
char filename [ PATH_MAX ] ;
char bf [ BUFSIZ ] ;
2009-06-02 00:50:19 +04:00
int fd , ret ;
2009-05-15 05:50:46 +04:00
size_t size ;
2009-06-02 00:50:19 +04:00
char * field , * sep ;
2009-06-02 16:13:24 +04:00
DIR * tasks ;
struct dirent dirent , * next ;
2009-05-15 05:50:46 +04:00
snprintf ( filename , sizeof ( filename ) , " /proc/%d/stat " , pid ) ;
fd = open ( filename , O_RDONLY ) ;
if ( fd < 0 ) {
fprintf ( stderr , " couldn't open %s \n " , filename ) ;
exit ( EXIT_FAILURE ) ;
}
if ( read ( fd , bf , sizeof ( bf ) ) < 0 ) {
fprintf ( stderr , " couldn't read %s \n " , filename ) ;
exit ( EXIT_FAILURE ) ;
}
close ( fd ) ;
2009-06-02 00:50:19 +04:00
/* 9027 (cat) R 6747 9027 6747 34816 9027 ... */
2009-05-15 05:50:46 +04:00
memset ( & comm_ev , 0 , sizeof ( comm_ev ) ) ;
2009-06-02 00:50:19 +04:00
field = strchr ( bf , ' ( ' ) ;
if ( field = = NULL )
goto out_failure ;
sep = strchr ( + + field , ' ) ' ) ;
if ( sep = = NULL )
goto out_failure ;
size = sep - field ;
memcpy ( comm_ev . comm , field , size + + ) ;
2009-06-02 16:13:24 +04:00
comm_ev . pid = pid ;
2009-05-15 05:50:46 +04:00
comm_ev . header . type = PERF_EVENT_COMM ;
2009-06-11 16:16:15 +04:00
size = ALIGN ( size , sizeof ( __u64 ) ) ;
2009-05-15 05:50:46 +04:00
comm_ev . header . size = sizeof ( comm_ev ) - ( sizeof ( comm_ev . comm ) - size ) ;
2009-05-27 11:10:38 +04:00
2009-06-02 16:13:24 +04:00
if ( ! full ) {
comm_ev . tid = pid ;
ret = write ( output , & comm_ev , comm_ev . header . size ) ;
if ( ret < 0 ) {
perror ( " failed to write " ) ;
exit ( - 1 ) ;
}
return ;
}
snprintf ( filename , sizeof ( filename ) , " /proc/%d/task " , pid ) ;
tasks = opendir ( filename ) ;
while ( ! readdir_r ( tasks , & dirent , & next ) & & next ) {
char * end ;
pid = strtol ( dirent . d_name , & end , 10 ) ;
if ( * end )
continue ;
comm_ev . tid = pid ;
ret = write ( output , & comm_ev , comm_ev . header . size ) ;
if ( ret < 0 ) {
perror ( " failed to write " ) ;
exit ( - 1 ) ;
}
2009-05-15 05:50:46 +04:00
}
2009-06-02 16:13:24 +04:00
closedir ( tasks ) ;
return ;
2009-06-02 00:50:19 +04:00
out_failure :
fprintf ( stderr , " couldn't get COMM and pgid, malformed %s \n " ,
filename ) ;
exit ( EXIT_FAILURE ) ;
2009-05-15 05:50:46 +04:00
}
2009-06-05 16:29:10 +04:00
static void pid_synthesize_mmap_samples ( pid_t pid )
2009-05-15 05:50:46 +04:00
{
char filename [ PATH_MAX ] ;
FILE * fp ;
snprintf ( filename , sizeof ( filename ) , " /proc/%d/maps " , pid ) ;
fp = fopen ( filename , " r " ) ;
if ( fp = = NULL ) {
fprintf ( stderr , " couldn't open %s \n " , filename ) ;
exit ( EXIT_FAILURE ) ;
}
while ( 1 ) {
2009-06-02 00:50:19 +04:00
char bf [ BUFSIZ ] , * pbf = bf ;
2009-05-15 05:50:46 +04:00
struct mmap_event mmap_ev = {
. header . type = PERF_EVENT_MMAP ,
} ;
2009-06-02 00:50:19 +04:00
int n ;
2009-05-15 05:50:46 +04:00
size_t size ;
if ( fgets ( bf , sizeof ( bf ) , fp ) = = NULL )
break ;
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
2009-06-02 00:50:19 +04:00
n = hex2u64 ( pbf , & mmap_ev . start ) ;
if ( n < 0 )
continue ;
pbf + = n + 1 ;
n = hex2u64 ( pbf , & mmap_ev . len ) ;
if ( n < 0 )
continue ;
pbf + = n + 3 ;
if ( * pbf = = ' x ' ) { /* vm_exec */
2009-05-15 05:50:46 +04:00
char * execname = strrchr ( bf , ' ' ) ;
if ( execname = = NULL | | execname [ 1 ] ! = ' / ' )
continue ;
execname + = 1 ;
size = strlen ( execname ) ;
execname [ size - 1 ] = ' \0 ' ; /* Remove \n */
memcpy ( mmap_ev . filename , execname , size ) ;
2009-06-11 16:16:15 +04:00
size = ALIGN ( size , sizeof ( __u64 ) ) ;
2009-05-15 05:50:46 +04:00
mmap_ev . len - = mmap_ev . start ;
mmap_ev . header . size = ( sizeof ( mmap_ev ) -
( sizeof ( mmap_ev . filename ) - size ) ) ;
2009-06-02 16:13:24 +04:00
mmap_ev . pid = pid ;
2009-05-15 05:50:46 +04:00
mmap_ev . tid = pid ;
if ( write ( output , & mmap_ev , mmap_ev . header . size ) < 0 ) {
perror ( " failed to write " ) ;
exit ( - 1 ) ;
}
}
}
fclose ( fp ) ;
}
2009-06-05 16:29:10 +04:00
static void synthesize_samples ( void )
2009-06-02 16:13:24 +04:00
{
DIR * proc ;
struct dirent dirent , * next ;
proc = opendir ( " /proc " ) ;
while ( ! readdir_r ( proc , & dirent , & next ) & & next ) {
char * end ;
pid_t pid ;
pid = strtol ( dirent . d_name , & end , 10 ) ;
if ( * end ) /* only interested in proper numerical dirents */
continue ;
pid_synthesize_comm_event ( pid , 1 ) ;
2009-06-05 16:29:10 +04:00
pid_synthesize_mmap_samples ( pid ) ;
2009-06-02 16:13:24 +04:00
}
closedir ( proc ) ;
}
2009-06-05 15:18:41 +04:00
static int group_fd ;
static void create_counter ( int counter , int cpu , pid_t pid )
2009-04-08 17:01:31 +04:00
{
2009-06-06 11:58:57 +04:00
struct perf_counter_attr * attr = attrs + counter ;
2009-05-05 19:50:27 +04:00
int track = 1 ;
2009-06-10 23:45:22 +04:00
attr - > sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID ;
2009-06-05 20:37:22 +04:00
if ( freq ) {
2009-06-10 23:45:22 +04:00
attr - > sample_type | = PERF_SAMPLE_PERIOD ;
2009-06-06 11:58:57 +04:00
attr - > freq = 1 ;
attr - > sample_freq = freq ;
2009-06-05 20:37:22 +04:00
}
2009-06-06 11:58:57 +04:00
attr - > mmap = track ;
attr - > comm = track ;
attr - > inherit = ( cpu < 0 ) & & inherit ;
2009-06-10 17:03:06 +04:00
attr - > disabled = 1 ;
2009-05-05 19:50:27 +04:00
2009-06-05 15:18:41 +04:00
track = 0 ; /* only the first counter needs these */
2009-05-05 19:50:27 +04:00
2009-06-07 19:39:02 +04:00
try_again :
2009-06-06 11:58:57 +04:00
fd [ nr_cpu ] [ counter ] = sys_perf_counter_open ( attr , pid , cpu , group_fd , 0 ) ;
2009-05-05 19:50:27 +04:00
2009-06-05 15:18:41 +04:00
if ( fd [ nr_cpu ] [ counter ] < 0 ) {
int err = errno ;
2009-05-05 19:50:27 +04:00
2009-06-05 15:18:41 +04:00
if ( err = = EPERM )
2009-06-07 19:39:02 +04:00
die ( " Permission error - are you root? \n " ) ;
/*
* If it ' s cycles then fall back to hrtimer
* based cpu - clock - tick sw counter , which
* is always available even if no PMU support :
*/
if ( attr - > type = = PERF_TYPE_HARDWARE
2009-06-11 16:06:28 +04:00
& & attr - > config = = PERF_COUNT_HW_CPU_CYCLES ) {
2009-06-07 19:39:02 +04:00
if ( verbose )
warning ( " ... trying to fall back to cpu-clock-ticks \n " ) ;
attr - > type = PERF_TYPE_SOFTWARE ;
2009-06-11 16:06:28 +04:00
attr - > config = PERF_COUNT_SW_CPU_CLOCK ;
2009-06-07 19:39:02 +04:00
goto try_again ;
}
2009-06-07 19:46:24 +04:00
printf ( " \n " ) ;
error ( " perfcounter syscall returned with %d (%s) \n " ,
fd [ nr_cpu ] [ counter ] , strerror ( err ) ) ;
die ( " No CONFIG_PERF_COUNTERS=y kernel support configured? \n " ) ;
2009-06-05 15:18:41 +04:00
exit ( - 1 ) ;
}
2009-06-07 19:39:02 +04:00
2009-06-05 15:18:41 +04:00
assert ( fd [ nr_cpu ] [ counter ] > = 0 ) ;
fcntl ( fd [ nr_cpu ] [ counter ] , F_SETFL , O_NONBLOCK ) ;
2009-05-05 19:50:27 +04:00
2009-06-05 15:18:41 +04:00
/*
* First counter acts as the group leader :
*/
if ( group & & group_fd = = - 1 )
group_fd = fd [ nr_cpu ] [ counter ] ;
event_array [ nr_poll ] . fd = fd [ nr_cpu ] [ counter ] ;
event_array [ nr_poll ] . events = POLLIN ;
nr_poll + + ;
mmap_array [ nr_cpu ] [ counter ] . counter = counter ;
mmap_array [ nr_cpu ] [ counter ] . prev = 0 ;
mmap_array [ nr_cpu ] [ counter ] . mask = mmap_pages * page_size - 1 ;
mmap_array [ nr_cpu ] [ counter ] . base = mmap ( NULL , ( mmap_pages + 1 ) * page_size ,
PROT_READ , MAP_SHARED , fd [ nr_cpu ] [ counter ] , 0 ) ;
if ( mmap_array [ nr_cpu ] [ counter ] . base = = MAP_FAILED ) {
error ( " failed to mmap with %d (%s) \n " , errno , strerror ( errno ) ) ;
exit ( - 1 ) ;
}
2009-06-10 17:03:06 +04:00
ioctl ( fd [ nr_cpu ] [ counter ] , PERF_COUNTER_IOC_ENABLE ) ;
2009-06-05 15:18:41 +04:00
}
2009-06-03 21:17:25 +04:00
2009-06-05 15:18:41 +04:00
static void open_counters ( int cpu , pid_t pid )
{
int counter ;
2009-05-05 19:50:27 +04:00
2009-06-05 15:18:41 +04:00
if ( pid > 0 ) {
pid_synthesize_comm_event ( pid , 0 ) ;
2009-06-05 16:29:10 +04:00
pid_synthesize_mmap_samples ( pid ) ;
2009-05-05 19:50:27 +04:00
}
2009-06-05 15:18:41 +04:00
group_fd = - 1 ;
for ( counter = 0 ; counter < nr_counters ; counter + + )
create_counter ( counter , cpu , pid ) ;
2009-05-05 19:50:27 +04:00
nr_cpu + + ;
}
2009-05-26 11:17:18 +04:00
static int __cmd_record ( int argc , const char * * argv )
2009-05-05 19:50:27 +04:00
{
int i , counter ;
2009-06-03 00:59:57 +04:00
struct stat st ;
2009-04-08 17:01:31 +04:00
pid_t pid ;
2009-06-03 00:59:57 +04:00
int flags ;
2009-04-08 17:01:31 +04:00
int ret ;
page_size = sysconf ( _SC_PAGE_SIZE ) ;
nr_cpus = sysconf ( _SC_NPROCESSORS_ONLN ) ;
assert ( nr_cpus < = MAX_NR_CPUS ) ;
assert ( nr_cpus > = 0 ) ;
2009-06-03 00:59:57 +04:00
if ( ! stat ( output_name , & st ) & & ! force & & ! append_file ) {
fprintf ( stderr , " Error, output file %s exists, use -A to append or -f to overwrite. \n " ,
2009-06-02 17:52:24 +04:00
output_name ) ;
exit ( - 1 ) ;
}
2009-06-03 00:59:57 +04:00
flags = O_CREAT | O_RDWR ;
if ( append_file )
flags | = O_APPEND ;
else
flags | = O_TRUNC ;
output = open ( output_name , flags , S_IRUSR | S_IWUSR ) ;
2009-04-08 17:01:31 +04:00
if ( output < 0 ) {
perror ( " failed to create output file " ) ;
exit ( - 1 ) ;
}
2009-05-15 05:50:46 +04:00
if ( ! system_wide ) {
2009-06-04 15:41:22 +04:00
open_counters ( - 1 , target_pid ! = - 1 ? target_pid : getpid ( ) ) ;
2009-05-15 05:50:46 +04:00
} else for ( i = 0 ; i < nr_cpus ; i + + )
open_counters ( i , target_pid ) ;
2009-04-08 17:01:31 +04:00
2009-06-10 17:55:59 +04:00
atexit ( sig_atexit ) ;
2009-05-05 19:50:27 +04:00
signal ( SIGCHLD , sig_handler ) ;
signal ( SIGINT , sig_handler ) ;
2009-04-08 17:01:31 +04:00
2009-05-27 12:10:51 +04:00
if ( target_pid = = - 1 & & argc ) {
2009-05-15 05:50:46 +04:00
pid = fork ( ) ;
if ( pid < 0 )
perror ( " failed to fork " ) ;
2009-04-08 17:01:31 +04:00
2009-05-15 05:50:46 +04:00
if ( ! pid ) {
2009-05-26 11:17:18 +04:00
if ( execvp ( argv [ 0 ] , ( char * * ) argv ) ) {
2009-05-15 05:50:46 +04:00
perror ( argv [ 0 ] ) ;
exit ( - 1 ) ;
}
2009-04-08 17:01:31 +04:00
}
}
if ( realtime_prio ) {
struct sched_param param ;
param . sched_priority = realtime_prio ;
if ( sched_setscheduler ( 0 , SCHED_FIFO , & param ) ) {
printf ( " Could not set realtime priority. \n " ) ;
exit ( - 1 ) ;
}
}
2009-06-02 16:13:24 +04:00
if ( system_wide )
2009-06-05 16:29:10 +04:00
synthesize_samples ( ) ;
2009-04-08 17:01:31 +04:00
while ( ! done ) {
2009-06-05 16:29:10 +04:00
int hits = samples ;
2009-04-08 17:01:31 +04:00
2009-05-05 19:50:27 +04:00
for ( i = 0 ; i < nr_cpu ; i + + ) {
2009-04-08 17:01:31 +04:00
for ( counter = 0 ; counter < nr_counters ; counter + + )
mmap_read ( & mmap_array [ i ] [ counter ] ) ;
}
2009-06-05 16:29:10 +04:00
if ( hits = = samples )
2009-04-08 17:01:31 +04:00
ret = poll ( event_array , nr_poll , 100 ) ;
}
2009-06-03 21:27:19 +04:00
/*
* Approximate RIP event size : 24 bytes .
*/
fprintf ( stderr ,
2009-06-05 16:29:10 +04:00
" [ perf record: Captured and wrote %.3f MB %s (~%lld samples) ] \n " ,
2009-06-03 21:27:19 +04:00
( double ) bytes_written / 1024.0 / 1024.0 ,
output_name ,
bytes_written / 24 ) ;
2009-06-03 01:43:11 +04:00
2009-04-08 17:01:31 +04:00
return 0 ;
}
2009-05-26 11:17:18 +04:00
static const char * const record_usage [ ] = {
2009-05-28 18:25:34 +04:00
" perf record [<options>] [<command>] " ,
" perf record [<options>] -- <command> [<options>] " ,
2009-05-26 11:17:18 +04:00
NULL
} ;
2009-05-26 11:17:18 +04:00
static const struct option options [ ] = {
2009-05-26 11:17:18 +04:00
OPT_CALLBACK ( ' e ' , " event " , NULL , " event " ,
2009-06-06 14:24:17 +04:00
" event selector. use 'perf list' to list available events " ,
parse_events ) ,
2009-05-26 11:17:18 +04:00
OPT_INTEGER ( ' p ' , " pid " , & target_pid ,
" record events on existing pid " ) ,
OPT_INTEGER ( ' r ' , " realtime " , & realtime_prio ,
" collect data with this RT SCHED_FIFO priority " ) ,
OPT_BOOLEAN ( ' a ' , " all-cpus " , & system_wide ,
" system-wide collection from all CPUs " ) ,
2009-06-03 00:59:57 +04:00
OPT_BOOLEAN ( ' A ' , " append " , & append_file ,
" append to the output file to do incremental profiling " ) ,
2009-06-02 17:52:24 +04:00
OPT_BOOLEAN ( ' f ' , " force " , & force ,
" overwrite existing data file " ) ,
2009-06-03 13:24:33 +04:00
OPT_LONG ( ' c ' , " count " , & default_interval ,
2009-06-03 00:59:57 +04:00
" event period to sample " ) ,
OPT_STRING ( ' o ' , " output " , & output_name , " file " ,
" output file name " ) ,
OPT_BOOLEAN ( ' i ' , " inherit " , & inherit ,
" child tasks inherit counters " ) ,
2009-06-05 15:27:02 +04:00
OPT_INTEGER ( ' F ' , " freq " , & freq ,
" profile at this frequency " ) ,
2009-06-03 00:59:57 +04:00
OPT_INTEGER ( ' m ' , " mmap-pages " , & mmap_pages ,
" number of mmap data pages " ) ,
2009-06-07 19:39:02 +04:00
OPT_BOOLEAN ( ' v ' , " verbose " , & verbose ,
" be more verbose (show counter open errors, etc) " ) ,
2009-05-26 11:17:18 +04:00
OPT_END ( )
} ;
int cmd_record ( int argc , const char * * argv , const char * prefix )
{
int counter ;
argc = parse_options ( argc , argv , options , record_usage , 0 ) ;
2009-05-27 12:10:51 +04:00
if ( ! argc & & target_pid = = - 1 & & ! system_wide )
2009-05-26 11:17:18 +04:00
usage_with_options ( record_usage , options ) ;
2009-06-06 11:58:57 +04:00
if ( ! nr_counters )
2009-05-26 11:17:18 +04:00
nr_counters = 1 ;
for ( counter = 0 ; counter < nr_counters ; counter + + ) {
2009-06-06 11:58:57 +04:00
if ( attrs [ counter ] . sample_period )
2009-05-26 11:17:18 +04:00
continue ;
2009-06-06 11:58:57 +04:00
attrs [ counter ] . sample_period = default_interval ;
2009-05-26 11:17:18 +04:00
}
return __cmd_record ( argc , argv ) ;
}