2009-10-09 01:17:38 +04:00
/*
* builtin - probe . c
*
* Builtin probe command : Set up probe events by C expression
*
* Written by Masami Hiramatsu < mhiramat @ redhat . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
*/
# define _GNU_SOURCE
# include <sys/utsname.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <errno.h>
# include <stdio.h>
# include <unistd.h>
# include <stdlib.h>
# include <string.h>
# undef _GNU_SOURCE
# include "perf.h"
# include "builtin.h"
# include "util/util.h"
2009-12-09 01:03:23 +03:00
# include "util/strlist.h"
2009-10-17 04:08:10 +04:00
# include "util/event.h"
# include "util/debug.h"
2009-12-17 01:24:00 +03:00
# include "util/debugfs.h"
2009-12-15 18:32:33 +03:00
# include "util/symbol.h"
# include "util/thread.h"
2009-10-09 01:17:38 +04:00
# include "util/parse-options.h"
# include "util/parse-events.h" /* For debugfs_path */
# include "util/probe-finder.h"
2009-12-01 03:19:58 +03:00
# include "util/probe-event.h"
2009-10-09 01:17:38 +04:00
# define MAX_PATH_LEN 256
/* Session management structure */
static struct {
2009-12-15 18:31:14 +03:00
bool need_dwarf ;
bool list_events ;
2009-12-15 18:32:25 +03:00
bool force_add ;
2010-01-06 17:45:34 +03:00
bool show_lines ;
2009-10-09 01:17:38 +04:00
int nr_probe ;
struct probe_point probes [ MAX_PROBES ] ;
2009-12-09 01:03:23 +03:00
struct strlist * dellist ;
2010-02-03 21:52:03 +03:00
struct map_groups kmap_groups ;
struct map * kmaps [ MAP__NR_TYPES ] ;
2010-01-06 17:45:34 +03:00
struct line_range line_range ;
2009-10-09 01:17:38 +04:00
} session ;
2009-12-01 03:20:17 +03:00
2009-10-27 23:43:10 +03:00
/* Parse an event definition. Note that any error must die. */
static void parse_probe_event ( const char * str )
2009-10-09 01:17:38 +04:00
{
struct probe_point * pp = & session . probes [ session . nr_probe ] ;
2009-10-21 23:34:06 +04:00
pr_debug ( " probe-definition(%d): %s \n " , session . nr_probe , str ) ;
2009-10-09 01:17:38 +04:00
if ( + + session . nr_probe = = MAX_PROBES )
2009-12-01 03:19:58 +03:00
die ( " Too many probes (> %d) are specified. " , MAX_PROBES ) ;
2009-10-09 01:17:38 +04:00
2009-12-01 03:19:58 +03:00
/* Parse perf-probe event into probe_point */
2009-12-15 18:31:14 +03:00
parse_perf_probe_event ( str , pp , & session . need_dwarf ) ;
2009-10-08 02:28:30 +04:00
2009-10-21 23:34:06 +04:00
pr_debug ( " %d arguments \n " , pp - > nr_args ) ;
2009-10-27 23:43:02 +03:00
}
2009-12-09 01:02:54 +03:00
static void parse_probe_event_argv ( int argc , const char * * argv )
{
int i , len ;
char * buf ;
/* Bind up rest arguments */
len = 0 ;
for ( i = 0 ; i < argc ; i + + )
len + = strlen ( argv [ i ] ) + 1 ;
buf = zalloc ( len + 1 ) ;
if ( ! buf )
die ( " Failed to allocate memory for binding arguments. " ) ;
len = 0 ;
for ( i = 0 ; i < argc ; i + + )
len + = sprintf ( & buf [ len ] , " %s " , argv [ i ] ) ;
parse_probe_event ( buf ) ;
free ( buf ) ;
}
2009-10-27 23:43:10 +03:00
static int opt_add_probe_event ( const struct option * opt __used ,
2009-10-27 23:43:02 +03:00
const char * str , int unset __used )
{
if ( str )
2009-10-27 23:43:10 +03:00
parse_probe_event ( str ) ;
2009-10-09 01:17:38 +04:00
return 0 ;
}
2009-12-09 01:03:23 +03:00
static int opt_del_probe_event ( const struct option * opt __used ,
const char * str , int unset __used )
{
if ( str ) {
if ( ! session . dellist )
session . dellist = strlist__new ( true , NULL ) ;
strlist__add ( session . dellist , str ) ;
}
return 0 ;
}
2009-12-15 18:32:40 +03:00
/* Currently just checking function name from symbol map */
static void evaluate_probe_point ( struct probe_point * pp )
{
struct symbol * sym ;
2010-02-03 21:52:03 +03:00
sym = map__find_symbol_by_name ( session . kmaps [ MAP__FUNCTION ] ,
pp - > function , NULL ) ;
2009-12-15 18:32:40 +03:00
if ( ! sym )
die ( " Kernel symbol \' %s \' not found - probe not added. " ,
pp - > function ) ;
}
2010-02-25 16:35:42 +03:00
# ifndef NO_DWARF_SUPPORT
2009-12-15 18:32:33 +03:00
static int open_vmlinux ( void )
2009-10-09 01:17:38 +04:00
{
2010-02-03 21:52:03 +03:00
if ( map__load ( session . kmaps [ MAP__FUNCTION ] , NULL ) < 0 ) {
2009-12-15 18:32:33 +03:00
pr_debug ( " Failed to load kernel map. \n " ) ;
return - EINVAL ;
2009-10-09 01:17:38 +04:00
}
2010-02-03 21:52:03 +03:00
pr_debug ( " Try to open %s \n " ,
session . kmaps [ MAP__FUNCTION ] - > dso - > long_name ) ;
return open ( session . kmaps [ MAP__FUNCTION ] - > dso - > long_name , O_RDONLY ) ;
2009-10-09 01:17:38 +04:00
}
2010-01-16 15:31:16 +03:00
static int opt_show_lines ( const struct option * opt __used ,
const char * str , int unset __used )
{
if ( str )
parse_line_range_desc ( str , & session . line_range ) ;
INIT_LIST_HEAD ( & session . line_range . line_list ) ;
session . show_lines = true ;
return 0 ;
}
2009-10-08 02:28:30 +04:00
# endif
2009-10-09 01:17:38 +04:00
static const char * const probe_usage [ ] = {
2009-10-27 23:43:02 +03:00
" perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...] " ,
" perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...] " ,
2009-12-09 01:03:23 +03:00
" perf probe [<options>] --del '[GROUP:]EVENT' ... " ,
2009-12-01 03:20:17 +03:00
" perf probe --list " ,
2010-02-25 16:35:42 +03:00
# ifndef NO_DWARF_SUPPORT
2010-01-06 17:45:34 +03:00
" perf probe --line 'LINEDESC' " ,
2010-02-25 16:35:12 +03:00
# endif
2009-10-09 01:17:38 +04:00
NULL
} ;
static const struct option options [ ] = {
2009-10-17 04:08:10 +04:00
OPT_BOOLEAN ( ' v ' , " verbose " , & verbose ,
" be more verbose (show parsed arguments, etc) " ) ,
2010-02-25 16:35:42 +03:00
# ifndef NO_DWARF_SUPPORT
2009-12-16 01:04:39 +03:00
OPT_STRING ( ' k ' , " vmlinux " , & symbol_conf . vmlinux_name ,
2009-12-15 18:32:33 +03:00
" file " , " vmlinux pathname " ) ,
2009-10-08 02:28:30 +04:00
# endif
2009-12-15 18:31:14 +03:00
OPT_BOOLEAN ( ' l ' , " list " , & session . list_events ,
" list up current probe events " ) ,
2009-12-09 01:03:23 +03:00
OPT_CALLBACK ( ' d ' , " del " , NULL , " [GROUP:]EVENT " , " delete a probe event. " ,
opt_del_probe_event ) ,
2009-10-27 23:43:02 +03:00
OPT_CALLBACK ( ' a ' , " add " , NULL ,
2010-02-25 16:35:42 +03:00
# ifdef NO_DWARF_SUPPORT
2010-02-25 16:36:12 +03:00
" [EVENT=]FUNC[+OFF|%return] [ARG ...] " ,
2009-10-08 02:28:30 +04:00
# else
2010-03-04 06:38:43 +03:00
" [EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT "
2010-02-25 16:36:12 +03:00
" [ARG ...] " ,
2009-10-08 02:28:30 +04:00
# endif
2009-10-09 01:17:38 +04:00
" probe point definition, where \n "
2009-12-15 18:32:18 +03:00
" \t \t GROUP: \t Group name (optional) \n "
" \t \t EVENT: \t Event name \n "
2009-10-09 01:17:38 +04:00
" \t \t FUNC: \t Function name \n "
2010-02-25 16:36:12 +03:00
" \t \t OFF: \t Offset from function entry (in byte) \n "
2009-10-27 23:43:10 +03:00
" \t \t %return: \t Put the probe at function return \n "
2010-02-25 16:35:42 +03:00
# ifdef NO_DWARF_SUPPORT
2009-10-08 02:28:30 +04:00
" \t \t ARG: \t Probe argument (only \n "
# else
2009-10-09 01:17:38 +04:00
" \t \t SRC: \t Source code path \n "
2010-02-25 16:36:12 +03:00
" \t \t RL: \t Relative line number from function entry. \n "
" \t \t AL: \t Absolute line number in file. \n "
" \t \t PT: \t Lazy expression of line code. \n "
2009-10-09 01:17:38 +04:00
" \t \t ARG: \t Probe argument (local variable name or \n "
2009-10-08 02:28:30 +04:00
# endif
2009-12-01 03:19:58 +03:00
" \t \t \t kprobe-tracer argument format.) \n " ,
2009-10-27 23:43:10 +03:00
opt_add_probe_event ) ,
2009-12-15 18:32:25 +03:00
OPT_BOOLEAN ( ' f ' , " force " , & session . force_add , " forcibly add events "
" with existing name " ) ,
2010-02-25 16:35:42 +03:00
# ifndef NO_DWARF_SUPPORT
2010-01-06 17:45:34 +03:00
OPT_CALLBACK ( ' L ' , " line " , NULL ,
" FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2] " ,
" Show source code lines. " , opt_show_lines ) ,
# endif
2009-10-09 01:17:38 +04:00
OPT_END ( )
} ;
2010-01-06 17:45:34 +03:00
/* Initialize symbol maps for vmlinux */
static void init_vmlinux ( void )
{
symbol_conf . sort_by_name = true ;
if ( symbol_conf . vmlinux_name = = NULL )
symbol_conf . try_vmlinux_path = true ;
else
pr_debug ( " Use vmlinux: %s \n " , symbol_conf . vmlinux_name ) ;
if ( symbol__init ( ) < 0 )
die ( " Failed to init symbol map. " ) ;
2010-02-03 21:52:03 +03:00
map_groups__init ( & session . kmap_groups ) ;
if ( map_groups__create_kernel_maps ( & session . kmap_groups ,
session . kmaps ) < 0 )
die ( " Failed to create kernel maps. " ) ;
2010-01-06 17:45:34 +03:00
}
2009-10-09 01:17:38 +04:00
int cmd_probe ( int argc , const char * * argv , const char * prefix __used )
{
2009-12-09 01:02:54 +03:00
int i , ret ;
2010-02-25 16:35:42 +03:00
# ifndef NO_DWARF_SUPPORT
2009-12-02 11:08:41 +03:00
int fd ;
# endif
2009-10-09 01:17:38 +04:00
struct probe_point * pp ;
argc = parse_options ( argc , argv , options , probe_usage ,
2009-10-27 23:43:02 +03:00
PARSE_OPT_STOP_AT_NON_OPTION ) ;
2009-12-15 18:31:28 +03:00
if ( argc > 0 ) {
if ( strcmp ( argv [ 0 ] , " - " ) = = 0 ) {
pr_warning ( " Error: '-' is not supported. \n " ) ;
usage_with_options ( probe_usage , options ) ;
}
2009-12-09 01:02:54 +03:00
parse_probe_event_argv ( argc , argv ) ;
2009-12-15 18:31:28 +03:00
}
2009-10-27 23:43:02 +03:00
2010-01-06 17:45:34 +03:00
if ( ( ! session . nr_probe & & ! session . dellist & & ! session . list_events & &
! session . show_lines ) )
2009-10-09 01:17:38 +04:00
usage_with_options ( probe_usage , options ) ;
2009-12-17 01:24:00 +03:00
if ( debugfs_valid_mountpoint ( debugfs_path ) < 0 )
die ( " Failed to find debugfs path. " ) ;
2009-12-15 18:31:14 +03:00
if ( session . list_events ) {
2009-12-09 01:03:23 +03:00
if ( session . nr_probe ! = 0 | | session . dellist ) {
pr_warning ( " Error: Don't use --list with "
" --add/--del. \n " ) ;
usage_with_options ( probe_usage , options ) ;
}
2010-01-06 17:45:34 +03:00
if ( session . show_lines ) {
pr_warning ( " Error: Don't use --list with --line. \n " ) ;
usage_with_options ( probe_usage , options ) ;
}
2009-12-01 03:20:17 +03:00
show_perf_probe_events ( ) ;
return 0 ;
}
2010-02-25 16:35:42 +03:00
# ifndef NO_DWARF_SUPPORT
2010-01-06 17:45:34 +03:00
if ( session . show_lines ) {
if ( session . nr_probe ! = 0 | | session . dellist ) {
pr_warning ( " Error: Don't use --line with "
" --add/--del. \n " ) ;
usage_with_options ( probe_usage , options ) ;
}
init_vmlinux ( ) ;
fd = open_vmlinux ( ) ;
if ( fd < 0 )
die ( " Could not open debuginfo file. " ) ;
ret = find_line_range ( fd , & session . line_range ) ;
if ( ret < = 0 )
die ( " Source line is not found. \n " ) ;
close ( fd ) ;
show_line_range ( & session . line_range ) ;
return 0 ;
}
# endif
2009-12-09 01:03:23 +03:00
if ( session . dellist ) {
del_trace_kprobe_events ( session . dellist ) ;
strlist__delete ( session . dellist ) ;
if ( session . nr_probe = = 0 )
return 0 ;
}
2010-01-06 17:45:34 +03:00
/* Add probes */
init_vmlinux ( ) ;
2009-12-15 18:32:33 +03:00
2009-10-08 02:28:30 +04:00
if ( session . need_dwarf )
2010-02-25 16:35:42 +03:00
# ifdef NO_DWARF_SUPPORT
2009-12-01 03:19:58 +03:00
die ( " Debuginfo-analysis is not supported " ) ;
2010-02-25 16:35:42 +03:00
# else /* !NO_DWARF_SUPPORT */
2009-12-01 03:19:27 +03:00
pr_debug ( " Some probes require debuginfo. \n " ) ;
2009-10-09 01:17:38 +04:00
2009-12-15 18:32:33 +03:00
fd = open_vmlinux ( ) ;
2009-11-04 03:12:30 +03:00
if ( fd < 0 ) {
if ( session . need_dwarf )
2009-12-09 01:03:09 +03:00
die ( " Could not open debuginfo file. " ) ;
2009-11-04 03:12:30 +03:00
2009-12-07 20:00:59 +03:00
pr_debug ( " Could not open vmlinux/module file. "
" Try to use symbols. \n " ) ;
2009-11-04 03:12:30 +03:00
goto end_dwarf ;
}
2009-10-09 01:17:38 +04:00
/* Searching probe points */
2009-12-09 01:02:54 +03:00
for ( i = 0 ; i < session . nr_probe ; i + + ) {
pp = & session . probes [ i ] ;
2009-10-09 01:17:38 +04:00
if ( pp - > found )
continue ;
lseek ( fd , SEEK_SET , 0 ) ;
2010-02-25 16:35:34 +03:00
ret = find_probe_point ( fd , pp ) ;
2009-12-15 18:31:35 +03:00
if ( ret > 0 )
continue ;
2009-12-15 18:32:47 +03:00
if ( ret = = 0 ) { /* No error but failed to find probe point. */
synthesize_perf_probe_point ( pp ) ;
die ( " Probe point '%s' not found. - probe not added. " ,
pp - > probes [ 0 ] ) ;
}
2009-12-15 18:31:35 +03:00
/* Error path */
if ( session . need_dwarf ) {
if ( ret = = - ENOENT )
pr_warning ( " No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y. \n " ) ;
die ( " Could not analyze debuginfo. " ) ;
}
pr_debug ( " An error occurred in debuginfo analysis. "
" Try to use symbols. \n " ) ;
break ;
2009-10-09 01:17:38 +04:00
}
close ( fd ) ;
2009-11-04 03:12:30 +03:00
end_dwarf :
2010-02-25 16:35:42 +03:00
# endif /* !NO_DWARF_SUPPORT */
2009-10-08 02:28:30 +04:00
2009-11-04 03:12:30 +03:00
/* Synthesize probes without dwarf */
2009-12-09 01:02:54 +03:00
for ( i = 0 ; i < session . nr_probe ; i + + ) {
pp = & session . probes [ i ] ;
2009-11-04 03:12:30 +03:00
if ( pp - > found ) /* This probe is already found. */
continue ;
2009-12-15 18:32:40 +03:00
evaluate_probe_point ( pp ) ;
2009-12-01 03:19:58 +03:00
ret = synthesize_trace_kprobe_event ( pp ) ;
2009-11-04 03:12:30 +03:00
if ( ret = = - E2BIG )
2009-12-01 03:19:58 +03:00
die ( " probe point definition becomes too long. " ) ;
2009-11-04 03:12:30 +03:00
else if ( ret < 0 )
die ( " Failed to synthesize a probe point. " ) ;
}
2009-10-09 01:17:38 +04:00
/* Settng up probe points */
2009-12-15 18:32:25 +03:00
add_trace_kprobe_events ( session . probes , session . nr_probe ,
session . force_add ) ;
2009-10-09 01:17:38 +04:00
return 0 ;
}