2013-03-07 21:45:20 +09:00
/*
* builtin - ftrace . c
*
* Copyright ( c ) 2013 LG Electronics , Namhyung Kim < namhyung @ kernel . org >
*
* Released under the GPL v2 .
*/
# include "builtin.h"
# include "perf.h"
# include <unistd.h>
# include <signal.h>
# include "debug.h"
# include <subcmd/parse-options.h>
# include "evlist.h"
# include "target.h"
# include "thread_map.h"
2017-01-31 20:38:29 +09:00
# include "util/config.h"
2013-03-07 21:45:20 +09:00
# define DEFAULT_TRACER "function_graph"
struct perf_ftrace {
struct perf_evlist * evlist ;
struct target target ;
const char * tracer ;
} ;
static bool done ;
static void sig_handler ( int sig __maybe_unused )
{
done = true ;
}
/*
* perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails , since
* we asked by setting its exec_error to the function below ,
* ftrace__workload_exec_failed_signal .
*
* XXX We need to handle this more appropriately , emitting an error , etc .
*/
static void ftrace__workload_exec_failed_signal ( int signo __maybe_unused ,
siginfo_t * info __maybe_unused ,
void * ucontext __maybe_unused )
{
/* workload_exec_errno = info->si_value.sival_int; */
done = true ;
}
static int write_tracing_file ( const char * name , const char * val )
{
char * file ;
int fd , ret = - 1 ;
ssize_t size = strlen ( val ) ;
file = get_tracing_file ( name ) ;
if ( ! file ) {
pr_debug ( " cannot get tracing file: %s \n " , name ) ;
return - 1 ;
}
fd = open ( file , O_WRONLY ) ;
if ( fd < 0 ) {
pr_debug ( " cannot open tracing file: %s \n " , name ) ;
goto out ;
}
if ( write ( fd , val , size ) = = size )
ret = 0 ;
else
pr_debug ( " write '%s' to tracing/%s failed \n " , val , name ) ;
close ( fd ) ;
out :
put_tracing_file ( file ) ;
return ret ;
}
static int reset_tracing_files ( struct perf_ftrace * ftrace __maybe_unused )
{
if ( write_tracing_file ( " tracing_on " , " 0 " ) < 0 )
return - 1 ;
if ( write_tracing_file ( " current_tracer " , " nop " ) < 0 )
return - 1 ;
if ( write_tracing_file ( " set_ftrace_pid " , " " ) < 0 )
return - 1 ;
return 0 ;
}
static int __cmd_ftrace ( struct perf_ftrace * ftrace , int argc , const char * * argv )
{
char * trace_file ;
int trace_fd ;
char * trace_pid ;
char buf [ 4096 ] ;
struct pollfd pollfd = {
. events = POLLIN ,
} ;
if ( geteuid ( ) ! = 0 ) {
pr_err ( " ftrace only works for root! \n " ) ;
return - 1 ;
}
if ( argc < 1 )
return - 1 ;
signal ( SIGINT , sig_handler ) ;
signal ( SIGUSR1 , sig_handler ) ;
signal ( SIGCHLD , sig_handler ) ;
reset_tracing_files ( ftrace ) ;
/* reset ftrace buffer */
if ( write_tracing_file ( " trace " , " 0 " ) < 0 )
goto out ;
if ( perf_evlist__prepare_workload ( ftrace - > evlist , & ftrace - > target ,
argv , false , ftrace__workload_exec_failed_signal ) < 0 )
goto out ;
if ( write_tracing_file ( " current_tracer " , ftrace - > tracer ) < 0 ) {
pr_err ( " failed to set current_tracer to %s \n " , ftrace - > tracer ) ;
goto out ;
}
if ( asprintf ( & trace_pid , " %d " , thread_map__pid ( ftrace - > evlist - > threads , 0 ) ) < 0 ) {
pr_err ( " failed to allocate pid string \n " ) ;
goto out ;
}
if ( write_tracing_file ( " set_ftrace_pid " , trace_pid ) < 0 ) {
pr_err ( " failed to set pid: %s \n " , trace_pid ) ;
goto out_free_pid ;
}
trace_file = get_tracing_file ( " trace_pipe " ) ;
if ( ! trace_file ) {
pr_err ( " failed to open trace_pipe \n " ) ;
goto out_free_pid ;
}
trace_fd = open ( trace_file , O_RDONLY ) ;
put_tracing_file ( trace_file ) ;
if ( trace_fd < 0 ) {
pr_err ( " failed to open trace_pipe \n " ) ;
goto out_free_pid ;
}
fcntl ( trace_fd , F_SETFL , O_NONBLOCK ) ;
pollfd . fd = trace_fd ;
if ( write_tracing_file ( " tracing_on " , " 1 " ) < 0 ) {
pr_err ( " can't enable tracing \n " ) ;
goto out_close_fd ;
}
perf_evlist__start_workload ( ftrace - > evlist ) ;
while ( ! done ) {
if ( poll ( & pollfd , 1 , - 1 ) < 0 )
break ;
if ( pollfd . revents & POLLIN ) {
int n = read ( trace_fd , buf , sizeof ( buf ) ) ;
if ( n < 0 )
break ;
if ( fwrite ( buf , n , 1 , stdout ) ! = 1 )
break ;
}
}
write_tracing_file ( " tracing_on " , " 0 " ) ;
/* read remaining buffer contents */
while ( true ) {
int n = read ( trace_fd , buf , sizeof ( buf ) ) ;
if ( n < = 0 )
break ;
if ( fwrite ( buf , n , 1 , stdout ) ! = 1 )
break ;
}
out_close_fd :
close ( trace_fd ) ;
out_free_pid :
free ( trace_pid ) ;
out :
reset_tracing_files ( ftrace ) ;
return done ? 0 : - 1 ;
}
2017-01-31 20:38:29 +09:00
static int perf_ftrace_config ( const char * var , const char * value , void * cb )
{
struct perf_ftrace * ftrace = cb ;
if ( prefixcmp ( var , " ftrace. " ) )
return 0 ;
if ( strcmp ( var , " ftrace.tracer " ) )
return - 1 ;
if ( ! strcmp ( value , " function_graph " ) | |
! strcmp ( value , " function " ) ) {
ftrace - > tracer = value ;
return 0 ;
}
pr_err ( " Please select \" function_graph \" (default) or \" function \" \n " ) ;
return - 1 ;
}
2013-03-07 21:45:20 +09:00
int cmd_ftrace ( int argc , const char * * argv , const char * prefix __maybe_unused )
{
int ret ;
struct perf_ftrace ftrace = {
2017-01-26 18:35:37 +09:00
. tracer = DEFAULT_TRACER ,
2013-03-07 21:45:20 +09:00
. target = { . uid = UINT_MAX , } ,
} ;
const char * const ftrace_usage [ ] = {
" perf ftrace [<options>] <command> " ,
" perf ftrace [<options>] -- <command> [<options>] " ,
NULL
} ;
const struct option ftrace_options [ ] = {
OPT_STRING ( ' t ' , " tracer " , & ftrace . tracer , " tracer " ,
2017-01-18 21:49:14 -03:00
" tracer to use: function_graph(default) or function " ) ,
2013-03-07 21:45:20 +09:00
OPT_INCR ( ' v ' , " verbose " , & verbose ,
" be more verbose " ) ,
OPT_END ( )
} ;
2017-01-31 20:38:29 +09:00
ret = perf_config ( perf_ftrace_config , & ftrace ) ;
if ( ret < 0 )
return - 1 ;
2013-03-07 21:45:20 +09:00
argc = parse_options ( argc , argv , ftrace_options , ftrace_usage ,
PARSE_OPT_STOP_AT_NON_OPTION ) ;
if ( ! argc )
usage_with_options ( ftrace_usage , ftrace_options ) ;
ftrace . evlist = perf_evlist__new ( ) ;
if ( ftrace . evlist = = NULL )
return - ENOMEM ;
ret = perf_evlist__create_maps ( ftrace . evlist , & ftrace . target ) ;
if ( ret < 0 )
goto out_delete_evlist ;
ret = __cmd_ftrace ( & ftrace , argc , argv ) ;
out_delete_evlist :
perf_evlist__delete ( ftrace . evlist ) ;
return ret ;
}