2009-12-01 03:19:58 +03:00
/*
2010-07-29 18:13:51 +04:00
* probe - event . c : perf - probe definition to probe_events format converter
2009-12-01 03:19:58 +03:00
*
* 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 .
*
*/
# 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>
2009-12-01 03:20:17 +03:00
# include <stdarg.h>
# include <limits.h>
2011-01-13 15:46:11 +03:00
# include <elf.h>
2009-12-01 03:19:58 +03:00
2010-03-17 01:05:30 +03:00
# include "util.h"
2009-12-01 03:19:58 +03:00
# include "event.h"
2009-12-01 03:20:05 +03:00
# include "string.h"
2009-12-01 03:20:17 +03:00
# include "strlist.h"
2009-12-01 03:19:58 +03:00
# include "debug.h"
2010-01-06 01:47:10 +03:00
# include "cache.h"
2010-01-06 17:45:34 +03:00
# include "color.h"
2010-03-17 01:05:37 +03:00
# include "symbol.h"
# include "thread.h"
2010-04-15 02:39:28 +04:00
# include "debugfs.h"
2010-03-22 19:10:26 +03:00
# include "trace-event.h" /* For __unused */
2009-12-01 03:19:58 +03:00
# include "probe-event.h"
2010-03-17 01:06:12 +03:00
# include "probe-finder.h"
2009-12-01 03:19:58 +03:00
# define MAX_CMDLEN 256
# define MAX_PROBE_ARGS 128
# define PERFPROBE_GROUP "probe"
2010-03-17 01:06:05 +03:00
bool probe_event_dry_run ; /* Dry run flag */
2010-04-12 21:17:42 +04:00
# define semantic_error(msg ...) pr_err("Semantic error :" msg)
2009-12-01 03:19:58 +03:00
2009-12-01 03:20:17 +03:00
/* If there is no space to write, returns -E2BIG. */
2009-12-07 20:00:53 +03:00
static int e_snprintf ( char * str , size_t size , const char * format , . . . )
__attribute__ ( ( format ( printf , 3 , 4 ) ) ) ;
2009-12-01 03:20:17 +03:00
static int e_snprintf ( char * str , size_t size , const char * format , . . . )
{
int ret ;
va_list ap ;
va_start ( ap , format ) ;
ret = vsnprintf ( str , size , format , ap ) ;
va_end ( ap ) ;
if ( ret > = ( int ) size )
ret = - E2BIG ;
return ret ;
}
2010-03-22 19:10:26 +03:00
static char * synthesize_perf_probe_point ( struct perf_probe_point * pp ) ;
2010-04-28 04:20:43 +04:00
static struct machine machine ;
2010-03-17 01:05:37 +03:00
2010-10-21 14:13:41 +04:00
/* Initialize symbol maps and path of vmlinux/modules */
2010-04-12 21:17:42 +04:00
static int init_vmlinux ( void )
2010-03-17 01:05:37 +03:00
{
2010-04-12 21:17:42 +04:00
int ret ;
2010-03-17 01:05:37 +03:00
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 ) ;
2010-04-12 21:17:42 +04:00
ret = symbol__init ( ) ;
if ( ret < 0 ) {
pr_debug ( " Failed to init symbol map. \n " ) ;
goto out ;
}
2010-03-17 01:05:37 +03:00
2010-10-21 14:13:41 +04:00
ret = machine__init ( & machine , " " , HOST_KERNEL_ID ) ;
2010-04-28 04:20:43 +04:00
if ( ret < 0 )
goto out ;
2010-10-21 14:13:41 +04:00
if ( machine__create_kernel_maps ( & machine ) < 0 ) {
2010-12-17 16:12:11 +03:00
pr_debug ( " machine__create_kernel_maps() failed. \n " ) ;
2010-10-21 14:13:41 +04:00
goto out ;
}
2010-04-12 21:17:42 +04:00
out :
if ( ret < 0 )
pr_warning ( " Failed to init vmlinux path. \n " ) ;
return ret ;
2010-03-17 01:05:37 +03:00
}
2010-10-21 14:13:41 +04:00
static struct symbol * __find_kernel_function_by_name ( const char * name ,
struct map * * mapp )
{
return machine__find_kernel_function_by_name ( & machine , name , mapp ,
NULL ) ;
}
2011-01-13 15:46:11 +03:00
static struct map * kernel_get_module_map ( const char * module )
{
struct rb_node * nd ;
struct map_groups * grp = & machine . kmaps ;
2011-06-27 11:27:51 +04:00
/* A file path -- this is an offline module */
if ( module & & strchr ( module , ' / ' ) )
return machine__new_module ( & machine , 0 , module ) ;
2011-01-13 15:46:11 +03:00
if ( ! module )
module = " kernel " ;
for ( nd = rb_first ( & grp - > maps [ MAP__FUNCTION ] ) ; nd ; nd = rb_next ( nd ) ) {
struct map * pos = rb_entry ( nd , struct map , rb_node ) ;
if ( strncmp ( pos - > dso - > short_name + 1 , module ,
pos - > dso - > short_name_len - 2 ) = = 0 ) {
return pos ;
}
}
return NULL ;
}
static struct dso * kernel_get_module_dso ( const char * module )
2010-10-21 14:13:41 +04:00
{
struct dso * dso ;
2010-12-10 16:06:03 +03:00
struct map * map ;
const char * vmlinux_name ;
2010-10-21 14:13:41 +04:00
if ( module ) {
list_for_each_entry ( dso , & machine . kernel_dsos , node ) {
if ( strncmp ( dso - > short_name + 1 , module ,
dso - > short_name_len - 2 ) = = 0 )
goto found ;
}
pr_debug ( " Failed to find module %s. \n " , module ) ;
return NULL ;
2010-12-10 16:06:03 +03:00
}
map = machine . vmlinux_maps [ MAP__FUNCTION ] ;
dso = map - > dso ;
vmlinux_name = symbol_conf . vmlinux_name ;
if ( vmlinux_name ) {
if ( dso__load_vmlinux ( dso , map , vmlinux_name , NULL ) < = 0 )
return NULL ;
2010-10-21 14:13:41 +04:00
} else {
2010-12-10 16:07:14 +03:00
if ( dso__load_vmlinux_path ( dso , map , NULL ) < = 0 ) {
2010-10-21 14:13:41 +04:00
pr_debug ( " Failed to load kernel map. \n " ) ;
return NULL ;
}
}
found :
2011-01-13 15:46:11 +03:00
return dso ;
}
const char * kernel_get_module_path ( const char * module )
{
struct dso * dso = kernel_get_module_dso ( module ) ;
return ( dso ) ? dso - > long_name : NULL ;
2010-10-21 14:13:41 +04:00
}
2010-03-22 19:10:26 +03:00
# ifdef DWARF_SUPPORT
2011-06-27 11:27:39 +04:00
/* Open new debuginfo of given module */
static struct debuginfo * open_debuginfo ( const char * module )
2010-03-17 01:05:37 +03:00
{
2011-06-27 11:27:51 +04:00
const char * path ;
2011-06-27 11:27:39 +04:00
2011-06-27 11:27:51 +04:00
/* A file path -- this is an offline module */
if ( module & & strchr ( module , ' / ' ) )
path = module ;
else {
path = kernel_get_module_path ( module ) ;
if ( ! path ) {
pr_err ( " Failed to find path of %s module. \n " ,
module ? : " kernel " ) ;
return NULL ;
}
2010-03-17 01:05:37 +03:00
}
2011-06-27 11:27:39 +04:00
return debuginfo__new ( path ) ;
2010-03-17 01:05:37 +03:00
}
2010-03-22 19:10:26 +03:00
2010-07-29 18:13:51 +04:00
/*
* Convert trace point to probe point with debuginfo
* Currently only handles kprobes .
*/
static int kprobe_convert_to_perf_probe ( struct probe_trace_point * tp ,
2010-10-21 14:13:41 +04:00
struct perf_probe_point * pp )
2010-03-22 19:10:26 +03:00
{
struct symbol * sym ;
2010-10-21 14:13:41 +04:00
struct map * map ;
u64 addr ;
int ret = - ENOENT ;
2011-06-27 11:27:39 +04:00
struct debuginfo * dinfo ;
2010-03-22 19:10:26 +03:00
2010-10-21 14:13:41 +04:00
sym = __find_kernel_function_by_name ( tp - > symbol , & map ) ;
2010-03-22 19:10:26 +03:00
if ( sym ) {
2010-10-21 14:13:41 +04:00
addr = map - > unmap_ip ( map , sym - > start + tp - > offset ) ;
2011-01-23 01:37:02 +03:00
pr_debug ( " try to find %s+%ld@% " PRIx64 " \n " , tp - > symbol ,
2010-10-21 14:13:41 +04:00
tp - > offset , addr ) ;
2011-06-27 11:27:39 +04:00
dinfo = debuginfo__new_online_kernel ( addr ) ;
if ( dinfo ) {
ret = debuginfo__find_probe_point ( dinfo ,
( unsigned long ) addr , pp ) ;
debuginfo__delete ( dinfo ) ;
} else {
pr_debug ( " Failed to open debuginfo at 0x% " PRIx64 " \n " ,
addr ) ;
ret = - ENOENT ;
}
2010-03-22 19:10:26 +03:00
}
if ( ret < = 0 ) {
2010-04-12 21:17:42 +04:00
pr_debug ( " Failed to find corresponding probes from "
" debuginfo. Use kprobe event information. \n " ) ;
2010-04-12 21:17:56 +04:00
pp - > function = strdup ( tp - > symbol ) ;
if ( pp - > function = = NULL )
return - ENOMEM ;
2010-03-22 19:10:26 +03:00
pp - > offset = tp - > offset ;
}
pp - > retprobe = tp - > retprobe ;
2010-04-12 21:17:42 +04:00
return 0 ;
2010-03-22 19:10:26 +03:00
}
2011-06-27 11:27:45 +04:00
static int add_module_to_probe_trace_events ( struct probe_trace_event * tevs ,
int ntevs , const char * module )
{
2011-06-27 11:27:51 +04:00
int i , ret = 0 ;
char * tmp ;
if ( ! module )
return 0 ;
tmp = strrchr ( module , ' / ' ) ;
if ( tmp ) {
/* This is a module path -- get the module name */
module = strdup ( tmp + 1 ) ;
if ( ! module )
return - ENOMEM ;
tmp = strchr ( module , ' . ' ) ;
if ( tmp )
* tmp = ' \0 ' ;
tmp = ( char * ) module ; /* For free() */
}
2011-06-27 11:27:45 +04:00
for ( i = 0 ; i < ntevs ; i + + ) {
tevs [ i ] . point . module = strdup ( module ) ;
2011-06-27 11:27:51 +04:00
if ( ! tevs [ i ] . point . module ) {
ret = - ENOMEM ;
break ;
}
2011-06-27 11:27:45 +04:00
}
2011-06-27 11:27:51 +04:00
if ( tmp )
free ( tmp ) ;
return ret ;
2011-06-27 11:27:45 +04:00
}
2010-03-22 19:10:26 +03:00
/* Try to find perf_probe_event with debuginfo */
2010-07-29 18:13:51 +04:00
static int try_to_find_probe_trace_events ( struct perf_probe_event * pev ,
2011-06-27 11:27:45 +04:00
struct probe_trace_event * * tevs ,
int max_tevs , const char * module )
2010-03-22 19:10:26 +03:00
{
bool need_dwarf = perf_probe_event_need_dwarf ( pev ) ;
2011-06-27 11:27:39 +04:00
struct debuginfo * dinfo = open_debuginfo ( module ) ;
2011-06-27 11:27:45 +04:00
int ntevs , ret = 0 ;
2010-03-22 19:10:26 +03:00
2011-06-27 11:27:39 +04:00
if ( ! dinfo ) {
2010-04-12 21:17:42 +04:00
if ( need_dwarf ) {
pr_warning ( " Failed to open debuginfo file. \n " ) ;
2011-06-27 11:27:39 +04:00
return - ENOENT ;
2010-04-12 21:17:42 +04:00
}
2011-06-27 11:27:39 +04:00
pr_debug ( " Could not open debuginfo. Try to use symbols. \n " ) ;
2010-03-22 19:10:26 +03:00
return 0 ;
}
2011-06-27 11:27:39 +04:00
/* Searching trace events corresponding to a probe event */
ntevs = debuginfo__find_trace_events ( dinfo , pev , tevs , max_tevs ) ;
debuginfo__delete ( dinfo ) ;
2010-03-22 19:10:26 +03:00
2010-04-12 21:17:42 +04:00
if ( ntevs > 0 ) { /* Succeeded to find trace events */
2010-07-29 18:13:51 +04:00
pr_debug ( " find %d probe_trace_events. \n " , ntevs ) ;
2011-06-27 11:27:45 +04:00
if ( module )
ret = add_module_to_probe_trace_events ( * tevs , ntevs ,
module ) ;
return ret < 0 ? ret : ntevs ;
2010-04-12 21:17:42 +04:00
}
2010-03-22 19:10:26 +03:00
2010-04-12 21:17:42 +04:00
if ( ntevs = = 0 ) { /* No error but failed to find probe point. */
pr_warning ( " Probe point '%s' not found. \n " ,
synthesize_perf_probe_point ( & pev - > point ) ) ;
return - ENOENT ;
}
/* Error path : ntevs < 0 */
2010-04-21 23:56:24 +04:00
pr_debug ( " An error occurred in debuginfo analysis (%d). \n " , ntevs ) ;
if ( ntevs = = - EBADF ) {
pr_warning ( " Warning: No dwarf info found in the vmlinux - "
" please rebuild kernel with CONFIG_DEBUG_INFO=y. \n " ) ;
if ( ! need_dwarf ) {
2010-12-17 16:12:11 +03:00
pr_debug ( " Trying to use symbols. \n " ) ;
2010-04-21 23:56:24 +04:00
return 0 ;
}
2010-03-22 19:10:26 +03:00
}
2010-04-21 23:56:24 +04:00
return ntevs ;
2010-03-22 19:10:26 +03:00
}
2010-07-09 13:28:59 +04:00
/*
* Find a src file from a DWARF tag path . Prepend optional source path prefix
* and chop off leading directories that do not exist . Result is passed back as
* a newly allocated path on success .
* Return 0 if file was found and readable , - errno otherwise .
*/
2010-07-09 13:29:11 +04:00
static int get_real_path ( const char * raw_path , const char * comp_dir ,
char * * new_path )
2010-07-09 13:28:59 +04:00
{
2010-07-09 13:29:11 +04:00
const char * prefix = symbol_conf . source_prefix ;
if ( ! prefix ) {
if ( raw_path [ 0 ] ! = ' / ' & & comp_dir )
/* If not an absolute path, try to use comp_dir */
prefix = comp_dir ;
else {
if ( access ( raw_path , R_OK ) = = 0 ) {
* new_path = strdup ( raw_path ) ;
return 0 ;
} else
return - errno ;
}
2010-07-09 13:28:59 +04:00
}
2010-07-09 13:29:11 +04:00
* new_path = malloc ( ( strlen ( prefix ) + strlen ( raw_path ) + 2 ) ) ;
2010-07-09 13:28:59 +04:00
if ( ! * new_path )
return - ENOMEM ;
for ( ; ; ) {
2010-07-09 13:29:11 +04:00
sprintf ( * new_path , " %s/%s " , prefix , raw_path ) ;
2010-07-09 13:28:59 +04:00
if ( access ( * new_path , R_OK ) = = 0 )
return 0 ;
2010-07-09 13:29:11 +04:00
if ( ! symbol_conf . source_prefix )
/* In case of searching comp_dir, don't retry */
return - errno ;
2010-07-09 13:28:59 +04:00
switch ( errno ) {
case ENAMETOOLONG :
case ENOENT :
case EROFS :
case EFAULT :
raw_path = strchr ( + + raw_path , ' / ' ) ;
if ( ! raw_path ) {
free ( * new_path ) ;
* new_path = NULL ;
return - ENOENT ;
}
continue ;
default :
free ( * new_path ) ;
* new_path = NULL ;
return - errno ;
}
}
}
2010-03-22 19:10:26 +03:00
# define LINEBUF_SIZE 256
# define NR_ADDITIONAL_LINES 2
2010-12-20 17:18:04 +03:00
static int __show_one_line ( FILE * fp , int l , bool skip , bool show_num )
2010-03-22 19:10:26 +03:00
{
char buf [ LINEBUF_SIZE ] ;
2010-12-20 17:18:01 +03:00
const char * color = show_num ? " " : PERF_COLOR_BLUE ;
const char * prefix = NULL ;
2010-03-22 19:10:26 +03:00
2010-12-20 17:18:01 +03:00
do {
2010-03-22 19:10:26 +03:00
if ( fgets ( buf , LINEBUF_SIZE , fp ) = = NULL )
goto error ;
2010-12-20 17:18:01 +03:00
if ( skip )
continue ;
if ( ! prefix ) {
prefix = show_num ? " %7d " : " " ;
color_fprintf ( stdout , color , prefix , l ) ;
2010-03-22 19:10:26 +03:00
}
2010-12-20 17:18:01 +03:00
color_fprintf ( stdout , color , " %s " , buf ) ;
} while ( strchr ( buf , ' \n ' ) = = NULL ) ;
2010-04-12 21:17:42 +04:00
2010-12-20 17:18:04 +03:00
return 1 ;
2010-03-22 19:10:26 +03:00
error :
2010-12-20 17:18:04 +03:00
if ( ferror ( fp ) ) {
2010-12-22 19:37:13 +03:00
pr_warning ( " File read error: %s \n " , strerror ( errno ) ) ;
2010-12-20 17:18:04 +03:00
return - 1 ;
}
return 0 ;
}
2010-04-12 21:17:42 +04:00
2010-12-20 17:18:04 +03:00
static int _show_one_line ( FILE * fp , int l , bool skip , bool show_num )
{
int rv = __show_one_line ( fp , l , skip , show_num ) ;
if ( rv = = 0 ) {
pr_warning ( " Source file is shorter than expected. \n " ) ;
rv = - 1 ;
}
return rv ;
2010-03-22 19:10:26 +03:00
}
2010-12-20 17:18:04 +03:00
# define show_one_line_with_num(f,l) _show_one_line(f,l,false,true)
# define show_one_line(f,l) _show_one_line(f,l,false,false)
# define skip_one_line(f,l) _show_one_line(f,l,true,false)
# define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false)
2010-03-22 19:10:26 +03:00
/*
* Show line - range always requires debuginfo to find source file and
* line number .
*/
2010-10-21 14:13:41 +04:00
int show_line_range ( struct line_range * lr , const char * module )
2010-03-22 19:10:26 +03:00
{
2010-04-15 02:39:42 +04:00
int l = 1 ;
2010-03-22 19:10:26 +03:00
struct line_node * ln ;
2011-06-27 11:27:39 +04:00
struct debuginfo * dinfo ;
2010-03-22 19:10:26 +03:00
FILE * fp ;
2011-06-27 11:27:39 +04:00
int ret ;
2010-07-09 13:28:59 +04:00
char * tmp ;
2010-03-22 19:10:26 +03:00
/* Search a line range */
2010-04-12 21:17:42 +04:00
ret = init_vmlinux ( ) ;
if ( ret < 0 )
return ret ;
2011-06-27 11:27:39 +04:00
dinfo = open_debuginfo ( module ) ;
if ( ! dinfo ) {
2010-04-12 21:17:42 +04:00
pr_warning ( " Failed to open debuginfo file. \n " ) ;
2011-06-27 11:27:39 +04:00
return - ENOENT ;
2010-04-12 21:17:42 +04:00
}
2011-06-27 11:27:39 +04:00
ret = debuginfo__find_line_range ( dinfo , lr ) ;
debuginfo__delete ( dinfo ) ;
2010-04-12 21:17:42 +04:00
if ( ret = = 0 ) {
pr_warning ( " Specified source line is not found. \n " ) ;
return - ENOENT ;
} else if ( ret < 0 ) {
pr_warning ( " Debuginfo analysis failed. (%d) \n " , ret ) ;
return ret ;
}
2010-03-22 19:10:26 +03:00
2010-07-09 13:28:59 +04:00
/* Convert source file path */
tmp = lr - > path ;
2010-07-09 13:29:11 +04:00
ret = get_real_path ( tmp , lr - > comp_dir , & lr - > path ) ;
2010-07-09 13:28:59 +04:00
free ( tmp ) ; /* Free old path */
if ( ret < 0 ) {
pr_warning ( " Failed to find source file. (%d) \n " , ret ) ;
return ret ;
}
2010-03-22 19:10:26 +03:00
setup_pager ( ) ;
if ( lr - > function )
2011-02-10 12:08:16 +03:00
fprintf ( stdout , " <%s@%s:%d> \n " , lr - > function , lr - > path ,
2010-03-22 19:10:26 +03:00
lr - > start - lr - > offset ) ;
else
2010-12-20 17:18:00 +03:00
fprintf ( stdout , " <%s:%d> \n " , lr - > path , lr - > start ) ;
2010-03-22 19:10:26 +03:00
fp = fopen ( lr - > path , " r " ) ;
2010-04-12 21:17:42 +04:00
if ( fp = = NULL ) {
pr_warning ( " Failed to open %s: %s \n " , lr - > path ,
strerror ( errno ) ) ;
return - errno ;
}
2010-03-22 19:10:26 +03:00
/* Skip to starting line number */
2010-12-20 17:18:02 +03:00
while ( l < lr - > start ) {
2010-12-20 17:18:04 +03:00
ret = skip_one_line ( fp , l + + ) ;
2010-12-20 17:18:02 +03:00
if ( ret < 0 )
goto end ;
}
2010-03-22 19:10:26 +03:00
list_for_each_entry ( ln , & lr - > line_list , list ) {
2010-12-20 17:18:02 +03:00
for ( ; ln - > line > l ; l + + ) {
2010-12-20 17:18:04 +03:00
ret = show_one_line ( fp , l - lr - > offset ) ;
2010-12-20 17:18:02 +03:00
if ( ret < 0 )
goto end ;
}
2010-12-20 17:18:04 +03:00
ret = show_one_line_with_num ( fp , l + + - lr - > offset ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
goto end ;
2010-03-22 19:10:26 +03:00
}
if ( lr - > end = = INT_MAX )
lr - > end = l + NR_ADDITIONAL_LINES ;
2010-12-20 17:18:04 +03:00
while ( l < = lr - > end ) {
ret = show_one_line_or_eof ( fp , l + + - lr - > offset ) ;
if ( ret < = 0 )
2010-12-20 17:18:02 +03:00
break ;
}
2010-04-12 21:17:42 +04:00
end :
2010-03-22 19:10:26 +03:00
fclose ( fp ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2010-03-22 19:10:26 +03:00
}
2011-06-27 11:27:39 +04:00
static int show_available_vars_at ( struct debuginfo * dinfo ,
struct perf_probe_event * pev ,
2011-01-20 17:15:39 +03:00
int max_vls , struct strfilter * _filter ,
bool externs )
2010-10-21 14:13:23 +04:00
{
char * buf ;
2011-01-20 17:15:39 +03:00
int ret , i , nvars ;
2010-10-21 14:13:23 +04:00
struct str_node * node ;
struct variable_list * vls = NULL , * vl ;
2011-01-20 17:15:39 +03:00
const char * var ;
2010-10-21 14:13:23 +04:00
buf = synthesize_perf_probe_point ( & pev - > point ) ;
if ( ! buf )
return - EINVAL ;
pr_debug ( " Searching variables at %s \n " , buf ) ;
2011-06-27 11:27:39 +04:00
ret = debuginfo__find_available_vars_at ( dinfo , pev , & vls ,
max_vls , externs ) ;
2011-01-20 17:15:39 +03:00
if ( ret < = 0 ) {
pr_err ( " Failed to find variables at %s (%d) \n " , buf , ret ) ;
goto end ;
}
/* Some variables are found */
fprintf ( stdout , " Available variables at %s \n " , buf ) ;
for ( i = 0 ; i < ret ; i + + ) {
vl = & vls [ i ] ;
/*
* A probe point might be converted to
* several trace points .
*/
fprintf ( stdout , " \t @<%s+%lu> \n " , vl - > point . symbol ,
vl - > point . offset ) ;
free ( vl - > point . symbol ) ;
nvars = 0 ;
if ( vl - > vars ) {
strlist__for_each ( node , vl - > vars ) {
var = strchr ( node - > s , ' \t ' ) + 1 ;
if ( strfilter__compare ( _filter , var ) ) {
2010-10-21 14:13:23 +04:00
fprintf ( stdout , " \t \t %s \n " , node - > s ) ;
2011-01-20 17:15:39 +03:00
nvars + + ;
}
}
strlist__delete ( vl - > vars ) ;
2010-10-21 14:13:23 +04:00
}
2011-01-20 17:15:39 +03:00
if ( nvars = = 0 )
fprintf ( stdout , " \t \t (No matched variables) \n " ) ;
}
free ( vls ) ;
end :
2010-10-21 14:13:23 +04:00
free ( buf ) ;
return ret ;
}
/* Show available variables on given probe point */
int show_available_vars ( struct perf_probe_event * pevs , int npevs ,
2011-01-20 17:15:39 +03:00
int max_vls , const char * module ,
struct strfilter * _filter , bool externs )
2010-10-21 14:13:23 +04:00
{
2011-06-27 11:27:39 +04:00
int i , ret = 0 ;
struct debuginfo * dinfo ;
2010-10-21 14:13:23 +04:00
ret = init_vmlinux ( ) ;
if ( ret < 0 )
return ret ;
2011-06-27 11:27:39 +04:00
dinfo = open_debuginfo ( module ) ;
if ( ! dinfo ) {
pr_warning ( " Failed to open debuginfo file. \n " ) ;
return - ENOENT ;
}
2010-10-21 14:13:23 +04:00
setup_pager ( ) ;
2011-06-27 11:27:39 +04:00
for ( i = 0 ; i < npevs & & ret > = 0 ; i + + )
ret = show_available_vars_at ( dinfo , & pevs [ i ] , max_vls , _filter ,
2011-01-20 17:15:39 +03:00
externs ) ;
2011-06-27 11:27:39 +04:00
debuginfo__delete ( dinfo ) ;
2010-10-21 14:13:23 +04:00
return ret ;
}
2010-03-22 19:10:26 +03:00
# else /* !DWARF_SUPPORT */
2010-07-29 18:13:51 +04:00
static int kprobe_convert_to_perf_probe ( struct probe_trace_point * tp ,
2010-10-21 14:13:41 +04:00
struct perf_probe_point * pp )
2010-03-22 19:10:26 +03:00
{
2010-10-21 14:13:41 +04:00
struct symbol * sym ;
sym = __find_kernel_function_by_name ( tp - > symbol , NULL ) ;
if ( ! sym ) {
pr_err ( " Failed to find symbol %s in kernel. \n " , tp - > symbol ) ;
return - ENOENT ;
}
2010-04-12 21:17:56 +04:00
pp - > function = strdup ( tp - > symbol ) ;
if ( pp - > function = = NULL )
return - ENOMEM ;
2010-03-22 19:10:26 +03:00
pp - > offset = tp - > offset ;
pp - > retprobe = tp - > retprobe ;
2010-04-12 21:17:42 +04:00
return 0 ;
2010-03-22 19:10:26 +03:00
}
2010-07-29 18:13:51 +04:00
static int try_to_find_probe_trace_events ( struct perf_probe_event * pev ,
struct probe_trace_event * * tevs __unused ,
2010-10-21 14:13:41 +04:00
int max_tevs __unused , const char * mod __unused )
2010-03-22 19:10:26 +03:00
{
2010-04-12 21:17:42 +04:00
if ( perf_probe_event_need_dwarf ( pev ) ) {
pr_warning ( " Debuginfo-analysis is not supported. \n " ) ;
return - ENOSYS ;
}
2010-03-22 19:10:26 +03:00
return 0 ;
}
2010-10-21 14:13:41 +04:00
int show_line_range ( struct line_range * lr __unused , const char * module __unused )
2010-03-22 19:10:26 +03:00
{
2010-04-12 21:17:42 +04:00
pr_warning ( " Debuginfo-analysis is not supported. \n " ) ;
return - ENOSYS ;
2010-03-22 19:10:26 +03:00
}
2010-10-21 14:13:23 +04:00
int show_available_vars ( struct perf_probe_event * pevs __unused ,
2010-10-21 14:13:41 +04:00
int npevs __unused , int max_vls __unused ,
2011-01-20 17:15:39 +03:00
const char * module __unused ,
struct strfilter * filter __unused ,
bool externs __unused )
2010-10-21 14:13:23 +04:00
{
pr_warning ( " Debuginfo-analysis is not supported. \n " ) ;
return - ENOSYS ;
}
2010-03-17 01:05:37 +03:00
# endif
2010-12-20 17:18:05 +03:00
static int parse_line_num ( char * * ptr , int * val , const char * what )
{
const char * start = * ptr ;
errno = 0 ;
* val = strtol ( * ptr , ptr , 0 ) ;
if ( errno | | * ptr = = start ) {
semantic_error ( " '%s' is not a valid number. \n " , what ) ;
return - EINVAL ;
}
return 0 ;
}
2010-12-20 17:18:03 +03:00
/*
* Stuff ' lr ' according to the line range described by ' arg ' .
* The line range syntax is described by :
*
* SRC [ : SLN [ + NUM | - ELN ] ]
2011-02-10 12:08:10 +03:00
* FNC [ @ SRC ] [ : SLN [ + NUM | - ELN ] ]
2010-12-20 17:18:03 +03:00
*/
2010-04-12 21:17:42 +04:00
int parse_line_range_desc ( const char * arg , struct line_range * lr )
2010-01-06 17:45:34 +03:00
{
2011-02-10 12:08:10 +03:00
char * range , * file , * name = strdup ( arg ) ;
2010-12-20 17:18:05 +03:00
int err ;
if ( ! name )
return - ENOMEM ;
lr - > start = 0 ;
lr - > end = INT_MAX ;
range = strchr ( name , ' : ' ) ;
if ( range ) {
* range + + = ' \0 ' ;
err = parse_line_num ( & range , & lr - > start , " start line " ) ;
if ( err )
goto err ;
if ( * range = = ' + ' | | * range = = ' - ' ) {
const char c = * range + + ;
err = parse_line_num ( & range , & lr - > end , " end line " ) ;
if ( err )
goto err ;
if ( c = = ' + ' ) {
lr - > end + = lr - > start ;
/*
* Adjust the number of lines here .
* If the number of lines = = 1 , the
* the end of line should be equal to
* the start of line .
*/
lr - > end - - ;
}
}
2010-12-20 17:18:03 +03:00
2010-04-15 02:39:42 +04:00
pr_debug ( " Line range is %d to %d \n " , lr - > start , lr - > end ) ;
2010-12-20 17:18:05 +03:00
err = - EINVAL ;
2010-04-15 02:39:42 +04:00
if ( lr - > start > lr - > end ) {
2010-01-06 17:45:34 +03:00
semantic_error ( " Start line must be smaller "
2010-04-12 21:17:42 +04:00
" than end line. \n " ) ;
2010-12-20 17:18:05 +03:00
goto err ;
2010-04-12 21:17:42 +04:00
}
2010-12-20 17:18:05 +03:00
if ( * range ! = ' \0 ' ) {
semantic_error ( " Tailing with invalid str '%s'. \n " , range ) ;
goto err ;
2010-04-12 21:17:42 +04:00
}
2010-04-15 02:39:42 +04:00
}
2010-04-12 21:17:56 +04:00
2011-02-10 12:08:10 +03:00
file = strchr ( name , ' @ ' ) ;
if ( file ) {
* file = ' \0 ' ;
lr - > file = strdup ( + + file ) ;
if ( lr - > file = = NULL ) {
err = - ENOMEM ;
goto err ;
}
lr - > function = name ;
} else if ( strchr ( name , ' . ' ) )
2010-12-20 17:18:05 +03:00
lr - > file = name ;
2010-01-06 17:45:34 +03:00
else
2010-12-20 17:18:05 +03:00
lr - > function = name ;
2010-04-12 21:17:42 +04:00
return 0 ;
2010-12-20 17:18:05 +03:00
err :
free ( name ) ;
return err ;
2010-01-06 17:45:34 +03:00
}
2009-12-17 01:24:15 +03:00
/* Check the name is good for event/group */
static bool check_event_name ( const char * name )
{
if ( ! isalpha ( * name ) & & * name ! = ' _ ' )
return false ;
while ( * + + name ! = ' \0 ' ) {
if ( ! isalpha ( * name ) & & ! isdigit ( * name ) & & * name ! = ' _ ' )
return false ;
}
return true ;
}
2009-12-01 03:19:58 +03:00
/* Parse probepoint definition. */
2010-04-12 21:17:42 +04:00
static int parse_perf_probe_point ( char * arg , struct perf_probe_event * pev )
2009-12-01 03:19:58 +03:00
{
2010-03-17 01:06:12 +03:00
struct perf_probe_point * pp = & pev - > point ;
2009-12-01 03:19:58 +03:00
char * ptr , * tmp ;
char c , nc = 0 ;
/*
* < Syntax >
2010-02-25 16:36:12 +03:00
* perf probe [ EVENT = ] SRC [ : LN | ; PTN ]
* perf probe [ EVENT = ] FUNC [ @ SRC ] [ + OFFS | % return | : LN | ; PAT ]
2009-12-15 18:32:18 +03:00
*
* TODO : Group name support
2009-12-01 03:19:58 +03:00
*/
2010-02-25 16:36:12 +03:00
ptr = strpbrk ( arg , " ;=@+% " ) ;
if ( ptr & & * ptr = = ' = ' ) { /* Event name */
2009-12-15 18:32:18 +03:00
* ptr = ' \0 ' ;
tmp = ptr + 1 ;
2010-04-12 21:17:42 +04:00
if ( strchr ( arg , ' : ' ) ) {
semantic_error ( " Group name is not supported yet. \n " ) ;
return - ENOTSUP ;
}
if ( ! check_event_name ( arg ) ) {
2009-12-17 01:24:15 +03:00
semantic_error ( " %s is bad for event name -it must "
2010-04-12 21:17:42 +04:00
" follow C symbol-naming rule. \n " , arg ) ;
return - EINVAL ;
}
2010-04-12 21:17:56 +04:00
pev - > event = strdup ( arg ) ;
if ( pev - > event = = NULL )
return - ENOMEM ;
2010-03-17 01:06:12 +03:00
pev - > group = NULL ;
2009-12-15 18:32:18 +03:00
arg = tmp ;
}
2010-02-25 16:36:12 +03:00
ptr = strpbrk ( arg , " ;:+@% " ) ;
2009-12-01 03:19:58 +03:00
if ( ptr ) {
nc = * ptr ;
* ptr + + = ' \0 ' ;
}
2010-04-12 21:17:56 +04:00
tmp = strdup ( arg ) ;
if ( tmp = = NULL )
return - ENOMEM ;
2009-12-01 03:19:58 +03:00
/* Check arg is function or file and copy it */
2010-04-12 21:17:56 +04:00
if ( strchr ( tmp , ' . ' ) ) /* File */
pp - > file = tmp ;
2009-12-01 03:19:58 +03:00
else /* Function */
2010-04-12 21:17:56 +04:00
pp - > function = tmp ;
2009-12-01 03:19:58 +03:00
/* Parse other options */
while ( ptr ) {
arg = ptr ;
c = nc ;
2010-02-25 16:36:12 +03:00
if ( c = = ' ; ' ) { /* Lazy pattern must be the last part */
2010-04-12 21:17:56 +04:00
pp - > lazy_line = strdup ( arg ) ;
if ( pp - > lazy_line = = NULL )
return - ENOMEM ;
2010-02-25 16:36:12 +03:00
break ;
}
ptr = strpbrk ( arg , " ;:+@% " ) ;
2009-12-01 03:19:58 +03:00
if ( ptr ) {
nc = * ptr ;
* ptr + + = ' \0 ' ;
}
switch ( c ) {
case ' : ' : /* Line number */
pp - > line = strtoul ( arg , & tmp , 0 ) ;
2010-04-12 21:17:42 +04:00
if ( * tmp ! = ' \0 ' ) {
2010-02-25 16:36:12 +03:00
semantic_error ( " There is non-digit char "
2010-04-12 21:17:42 +04:00
" in line number. \n " ) ;
return - EINVAL ;
}
2009-12-01 03:19:58 +03:00
break ;
case ' + ' : /* Byte offset from a symbol */
pp - > offset = strtoul ( arg , & tmp , 0 ) ;
2010-04-12 21:17:42 +04:00
if ( * tmp ! = ' \0 ' ) {
2010-02-25 16:36:12 +03:00
semantic_error ( " There is non-digit character "
2010-04-12 21:17:42 +04:00
" in offset. \n " ) ;
return - EINVAL ;
}
2009-12-01 03:19:58 +03:00
break ;
case ' @ ' : /* File name */
2010-04-12 21:17:42 +04:00
if ( pp - > file ) {
semantic_error ( " SRC@SRC is not allowed. \n " ) ;
return - EINVAL ;
}
2010-04-12 21:17:56 +04:00
pp - > file = strdup ( arg ) ;
if ( pp - > file = = NULL )
return - ENOMEM ;
2009-12-01 03:19:58 +03:00
break ;
case ' % ' : /* Probe places */
if ( strcmp ( arg , " return " ) = = 0 ) {
pp - > retprobe = 1 ;
2010-04-12 21:17:42 +04:00
} else { /* Others not supported yet */
semantic_error ( " %%%s is not supported. \n " , arg ) ;
return - ENOTSUP ;
}
2009-12-01 03:19:58 +03:00
break ;
2010-04-12 21:17:42 +04:00
default : /* Buggy case */
pr_err ( " This program has a bug at %s:%d. \n " ,
__FILE__ , __LINE__ ) ;
return - ENOTSUP ;
2009-12-01 03:19:58 +03:00
break ;
}
}
/* Exclusion check */
2010-04-12 21:17:42 +04:00
if ( pp - > lazy_line & & pp - > line ) {
2010-12-17 16:12:11 +03:00
semantic_error ( " Lazy pattern can't be used with "
" line number. \n " ) ;
2010-04-12 21:17:42 +04:00
return - EINVAL ;
}
2010-02-25 16:36:12 +03:00
2010-04-12 21:17:42 +04:00
if ( pp - > lazy_line & & pp - > offset ) {
2010-12-17 16:12:11 +03:00
semantic_error ( " Lazy pattern can't be used with offset. \n " ) ;
2010-04-12 21:17:42 +04:00
return - EINVAL ;
}
2010-02-25 16:36:12 +03:00
2010-04-12 21:17:42 +04:00
if ( pp - > line & & pp - > offset ) {
2010-12-17 16:12:11 +03:00
semantic_error ( " Offset can't be used with line number. \n " ) ;
2010-04-12 21:17:42 +04:00
return - EINVAL ;
}
2009-12-01 03:19:58 +03:00
2010-04-12 21:17:42 +04:00
if ( ! pp - > line & & ! pp - > lazy_line & & pp - > file & & ! pp - > function ) {
2010-02-25 16:36:12 +03:00
semantic_error ( " File always requires line number or "
2010-12-17 16:12:11 +03:00
" lazy pattern. \n " ) ;
2010-04-12 21:17:42 +04:00
return - EINVAL ;
}
2009-12-01 03:19:58 +03:00
2010-04-12 21:17:42 +04:00
if ( pp - > offset & & ! pp - > function ) {
2010-12-17 16:12:11 +03:00
semantic_error ( " Offset requires an entry function. \n " ) ;
2010-04-12 21:17:42 +04:00
return - EINVAL ;
}
2009-12-01 03:19:58 +03:00
2010-04-12 21:17:42 +04:00
if ( pp - > retprobe & & ! pp - > function ) {
2010-12-17 16:12:11 +03:00
semantic_error ( " Return probe requires an entry function. \n " ) ;
2010-04-12 21:17:42 +04:00
return - EINVAL ;
}
2009-12-01 03:19:58 +03:00
2010-04-12 21:17:42 +04:00
if ( ( pp - > offset | | pp - > line | | pp - > lazy_line ) & & pp - > retprobe ) {
2010-02-25 16:36:12 +03:00
semantic_error ( " Offset/Line/Lazy pattern can't be used with "
2010-12-17 16:12:11 +03:00
" return probe. \n " ) ;
2010-04-12 21:17:42 +04:00
return - EINVAL ;
}
2009-12-01 03:19:58 +03:00
2010-03-17 01:06:12 +03:00
pr_debug ( " symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s \n " ,
2010-02-25 16:36:12 +03:00
pp - > function , pp - > file , pp - > line , pp - > offset , pp - > retprobe ,
pp - > lazy_line ) ;
2010-04-12 21:17:42 +04:00
return 0 ;
2009-12-01 03:19:58 +03:00
}
2010-03-17 01:06:26 +03:00
/* Parse perf-probe event argument */
2010-04-12 21:17:42 +04:00
static int parse_perf_probe_arg ( char * str , struct perf_probe_arg * arg )
2010-03-17 01:06:26 +03:00
{
2010-05-19 23:57:42 +04:00
char * tmp , * goodname ;
2010-03-17 01:06:26 +03:00
struct perf_probe_arg_field * * fieldp ;
pr_debug ( " parsing arg: %s into " , str ) ;
2010-04-12 21:16:53 +04:00
tmp = strchr ( str , ' = ' ) ;
if ( tmp ) {
2010-04-12 21:17:56 +04:00
arg - > name = strndup ( str , tmp - str ) ;
if ( arg - > name = = NULL )
return - ENOMEM ;
2010-04-12 21:17:22 +04:00
pr_debug ( " name:%s " , arg - > name ) ;
2010-04-12 21:16:53 +04:00
str = tmp + 1 ;
}
2010-04-12 21:17:22 +04:00
tmp = strchr ( str , ' : ' ) ;
if ( tmp ) { /* Type setting */
* tmp = ' \0 ' ;
2010-04-12 21:17:56 +04:00
arg - > type = strdup ( tmp + 1 ) ;
if ( arg - > type = = NULL )
return - ENOMEM ;
2010-04-12 21:17:22 +04:00
pr_debug ( " type:%s " , arg - > type ) ;
}
2010-05-19 23:57:42 +04:00
tmp = strpbrk ( str , " -.[ " ) ;
2010-03-17 01:06:26 +03:00
if ( ! is_c_varname ( str ) | | ! tmp ) {
/* A variable, register, symbol or special value */
2010-04-12 21:17:56 +04:00
arg - > var = strdup ( str ) ;
if ( arg - > var = = NULL )
return - ENOMEM ;
2010-04-12 21:16:53 +04:00
pr_debug ( " %s \n " , arg - > var ) ;
2010-04-12 21:17:42 +04:00
return 0 ;
2010-03-17 01:06:26 +03:00
}
2010-05-19 23:57:42 +04:00
/* Structure fields or array element */
2010-04-12 21:17:56 +04:00
arg - > var = strndup ( str , tmp - str ) ;
if ( arg - > var = = NULL )
return - ENOMEM ;
2010-05-19 23:57:42 +04:00
goodname = arg - > var ;
2010-04-12 21:16:53 +04:00
pr_debug ( " %s, " , arg - > var ) ;
2010-03-17 01:06:26 +03:00
fieldp = & arg - > field ;
do {
2010-04-12 21:17:49 +04:00
* fieldp = zalloc ( sizeof ( struct perf_probe_arg_field ) ) ;
if ( * fieldp = = NULL )
return - ENOMEM ;
2010-05-19 23:57:42 +04:00
if ( * tmp = = ' [ ' ) { /* Array */
str = tmp ;
( * fieldp ) - > index = strtol ( str + 1 , & tmp , 0 ) ;
2010-03-17 01:06:26 +03:00
( * fieldp ) - > ref = true ;
2010-05-19 23:57:42 +04:00
if ( * tmp ! = ' ] ' | | tmp = = str + 1 ) {
semantic_error ( " Array index must be a "
" number. \n " ) ;
return - EINVAL ;
}
tmp + + ;
if ( * tmp = = ' \0 ' )
tmp = NULL ;
} else { /* Structure */
if ( * tmp = = ' . ' ) {
str = tmp + 1 ;
( * fieldp ) - > ref = false ;
} else if ( tmp [ 1 ] = = ' > ' ) {
str = tmp + 2 ;
( * fieldp ) - > ref = true ;
} else {
semantic_error ( " Argument parse error: %s \n " ,
str ) ;
return - EINVAL ;
}
tmp = strpbrk ( str , " -.[ " ) ;
2010-04-12 21:17:42 +04:00
}
2010-03-17 01:06:26 +03:00
if ( tmp ) {
2010-04-12 21:17:56 +04:00
( * fieldp ) - > name = strndup ( str , tmp - str ) ;
if ( ( * fieldp ) - > name = = NULL )
return - ENOMEM ;
2010-05-19 23:57:42 +04:00
if ( * str ! = ' [ ' )
goodname = ( * fieldp ) - > name ;
2010-03-17 01:06:26 +03:00
pr_debug ( " %s(%d), " , ( * fieldp ) - > name , ( * fieldp ) - > ref ) ;
fieldp = & ( * fieldp ) - > next ;
}
} while ( tmp ) ;
2010-04-12 21:17:56 +04:00
( * fieldp ) - > name = strdup ( str ) ;
if ( ( * fieldp ) - > name = = NULL )
return - ENOMEM ;
2010-05-19 23:57:42 +04:00
if ( * str ! = ' [ ' )
goodname = ( * fieldp ) - > name ;
2010-03-17 01:06:26 +03:00
pr_debug ( " %s(%d) \n " , ( * fieldp ) - > name , ( * fieldp ) - > ref ) ;
2010-04-12 21:17:00 +04:00
2010-05-19 23:57:42 +04:00
/* If no name is specified, set the last field name (not array index)*/
2010-04-12 21:17:56 +04:00
if ( ! arg - > name ) {
2010-05-19 23:57:42 +04:00
arg - > name = strdup ( goodname ) ;
2010-04-12 21:17:56 +04:00
if ( arg - > name = = NULL )
return - ENOMEM ;
}
2010-04-12 21:17:42 +04:00
return 0 ;
2010-03-17 01:06:26 +03:00
}
2010-03-17 01:06:12 +03:00
/* Parse perf-probe event command */
2010-04-12 21:17:42 +04:00
int parse_perf_probe_command ( const char * cmd , struct perf_probe_event * pev )
2009-12-01 03:19:58 +03:00
{
2009-12-01 03:20:05 +03:00
char * * argv ;
2010-04-12 21:17:42 +04:00
int argc , i , ret = 0 ;
2009-12-15 18:31:14 +03:00
2010-03-17 01:06:12 +03:00
argv = argv_split ( cmd , & argc ) ;
2010-04-12 21:17:42 +04:00
if ( ! argv ) {
pr_debug ( " Failed to split arguments. \n " ) ;
return - ENOMEM ;
}
if ( argc - 1 > MAX_PROBE_ARGS ) {
semantic_error ( " Too many probe arguments (%d). \n " , argc - 1 ) ;
ret = - ERANGE ;
goto out ;
}
2009-12-01 03:19:58 +03:00
/* Parse probe point */
2010-04-12 21:17:42 +04:00
ret = parse_perf_probe_point ( argv [ 0 ] , pev ) ;
if ( ret < 0 )
goto out ;
2009-12-01 03:19:58 +03:00
2009-12-01 03:20:05 +03:00
/* Copy arguments and ensure return probe has no C argument */
2010-03-17 01:06:12 +03:00
pev - > nargs = argc - 1 ;
2010-04-12 21:17:49 +04:00
pev - > args = zalloc ( sizeof ( struct perf_probe_arg ) * pev - > nargs ) ;
if ( pev - > args = = NULL ) {
ret = - ENOMEM ;
goto out ;
}
2010-04-12 21:17:42 +04:00
for ( i = 0 ; i < pev - > nargs & & ret > = 0 ; i + + ) {
ret = parse_perf_probe_arg ( argv [ i + 1 ] , & pev - > args [ i ] ) ;
if ( ret > = 0 & &
is_c_varname ( pev - > args [ i ] . var ) & & pev - > point . retprobe ) {
2010-03-17 01:06:12 +03:00
semantic_error ( " You can't specify local variable for "
2010-04-12 21:17:42 +04:00
" kretprobe. \n " ) ;
ret = - EINVAL ;
}
2009-12-01 03:20:05 +03:00
}
2010-04-12 21:17:42 +04:00
out :
2009-12-01 03:20:05 +03:00
argv_free ( argv ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2009-12-01 03:19:58 +03:00
}
2010-03-17 01:06:12 +03:00
/* Return true if this perf_probe_event requires debuginfo */
bool perf_probe_event_need_dwarf ( struct perf_probe_event * pev )
{
int i ;
if ( pev - > point . file | | pev - > point . line | | pev - > point . lazy_line )
return true ;
for ( i = 0 ; i < pev - > nargs ; i + + )
2010-04-12 21:16:53 +04:00
if ( is_c_varname ( pev - > args [ i ] . var ) )
2010-03-17 01:06:12 +03:00
return true ;
return false ;
}
2010-07-29 18:13:51 +04:00
/* Parse probe_events event into struct probe_point */
static int parse_probe_trace_command ( const char * cmd ,
2011-06-27 11:27:45 +04:00
struct probe_trace_event * tev )
2009-12-01 03:20:17 +03:00
{
2010-07-29 18:13:51 +04:00
struct probe_trace_point * tp = & tev - > point ;
2009-12-01 03:20:17 +03:00
char pr ;
char * p ;
int ret , i , argc ;
char * * argv ;
2010-07-29 18:13:51 +04:00
pr_debug ( " Parsing probe_events: %s \n " , cmd ) ;
2010-03-17 01:06:12 +03:00
argv = argv_split ( cmd , & argc ) ;
2010-04-12 21:17:42 +04:00
if ( ! argv ) {
pr_debug ( " Failed to split arguments. \n " ) ;
return - ENOMEM ;
}
if ( argc < 2 ) {
semantic_error ( " Too few probe arguments. \n " ) ;
ret = - ERANGE ;
goto out ;
}
2009-12-01 03:20:17 +03:00
/* Scan event and group name. */
2009-12-02 11:42:54 +03:00
ret = sscanf ( argv [ 0 ] , " %c:%a[^/ \t ]/%a[^ \t ] " ,
2010-03-17 01:06:12 +03:00
& pr , ( float * ) ( void * ) & tev - > group ,
( float * ) ( void * ) & tev - > event ) ;
2010-04-12 21:17:42 +04:00
if ( ret ! = 3 ) {
semantic_error ( " Failed to parse event name: %s \n " , argv [ 0 ] ) ;
ret = - EINVAL ;
goto out ;
}
2010-03-17 01:06:12 +03:00
pr_debug ( " Group:%s Event:%s probe:%c \n " , tev - > group , tev - > event , pr ) ;
2009-12-01 03:20:17 +03:00
2010-03-17 01:06:12 +03:00
tp - > retprobe = ( pr = = ' r ' ) ;
2009-12-01 03:20:17 +03:00
2011-06-27 11:27:45 +04:00
/* Scan module name(if there), function name and offset */
p = strchr ( argv [ 1 ] , ' : ' ) ;
if ( p ) {
tp - > module = strndup ( argv [ 1 ] , p - argv [ 1 ] ) ;
p + + ;
} else
p = argv [ 1 ] ;
ret = sscanf ( p , " %a[^+]+%lu " , ( float * ) ( void * ) & tp - > symbol ,
2010-03-17 01:06:12 +03:00
& tp - > offset ) ;
2009-12-01 03:20:17 +03:00
if ( ret = = 1 )
2010-03-17 01:06:12 +03:00
tp - > offset = 0 ;
2009-12-01 03:20:17 +03:00
2010-03-17 01:06:12 +03:00
tev - > nargs = argc - 2 ;
2010-07-29 18:13:51 +04:00
tev - > args = zalloc ( sizeof ( struct probe_trace_arg ) * tev - > nargs ) ;
2010-04-12 21:17:49 +04:00
if ( tev - > args = = NULL ) {
ret = - ENOMEM ;
goto out ;
}
2010-03-17 01:06:12 +03:00
for ( i = 0 ; i < tev - > nargs ; i + + ) {
2009-12-01 03:20:17 +03:00
p = strchr ( argv [ i + 2 ] , ' = ' ) ;
if ( p ) /* We don't need which register is assigned. */
2010-03-17 01:06:12 +03:00
* p + + = ' \0 ' ;
else
p = argv [ i + 2 ] ;
2010-04-12 21:17:56 +04:00
tev - > args [ i ] . name = strdup ( argv [ i + 2 ] ) ;
2010-03-17 01:06:12 +03:00
/* TODO: parse regs and offset */
2010-04-12 21:17:56 +04:00
tev - > args [ i ] . value = strdup ( p ) ;
if ( tev - > args [ i ] . name = = NULL | | tev - > args [ i ] . value = = NULL ) {
ret = - ENOMEM ;
goto out ;
}
2009-12-01 03:20:17 +03:00
}
2010-04-12 21:17:42 +04:00
ret = 0 ;
out :
2009-12-01 03:20:17 +03:00
argv_free ( argv ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2009-12-01 03:20:17 +03:00
}
2010-03-17 01:06:26 +03:00
/* Compose only probe arg */
int synthesize_perf_probe_arg ( struct perf_probe_arg * pa , char * buf , size_t len )
{
struct perf_probe_arg_field * field = pa - > field ;
int ret ;
char * tmp = buf ;
2010-04-12 21:16:53 +04:00
if ( pa - > name & & pa - > var )
ret = e_snprintf ( tmp , len , " %s=%s " , pa - > name , pa - > var ) ;
else
ret = e_snprintf ( tmp , len , " %s " , pa - > name ? pa - > name : pa - > var ) ;
2010-03-17 01:06:26 +03:00
if ( ret < = 0 )
goto error ;
tmp + = ret ;
len - = ret ;
while ( field ) {
2010-05-19 23:57:42 +04:00
if ( field - > name [ 0 ] = = ' [ ' )
ret = e_snprintf ( tmp , len , " %s " , field - > name ) ;
else
ret = e_snprintf ( tmp , len , " %s%s " ,
field - > ref ? " -> " : " . " , field - > name ) ;
2010-03-17 01:06:26 +03:00
if ( ret < = 0 )
goto error ;
tmp + = ret ;
len - = ret ;
field = field - > next ;
}
2010-04-12 21:17:22 +04:00
if ( pa - > type ) {
ret = e_snprintf ( tmp , len , " :%s " , pa - > type ) ;
if ( ret < = 0 )
goto error ;
tmp + = ret ;
len - = ret ;
}
2010-03-17 01:06:26 +03:00
return tmp - buf ;
error :
2010-12-17 16:12:11 +03:00
pr_debug ( " Failed to synthesize perf probe argument: %s \n " ,
2010-04-12 21:17:42 +04:00
strerror ( - ret ) ) ;
return ret ;
2010-03-17 01:06:26 +03:00
}
2010-03-17 01:06:12 +03:00
/* Compose only probe point (not argument) */
static char * synthesize_perf_probe_point ( struct perf_probe_point * pp )
2009-12-01 03:20:17 +03:00
{
2010-03-17 01:06:19 +03:00
char * buf , * tmp ;
char offs [ 32 ] = " " , line [ 32 ] = " " , file [ 32 ] = " " ;
int ret , len ;
2009-12-01 03:20:17 +03:00
2010-04-12 21:17:49 +04:00
buf = zalloc ( MAX_CMDLEN ) ;
if ( buf = = NULL ) {
ret = - ENOMEM ;
goto error ;
}
2009-12-01 03:20:17 +03:00
if ( pp - > offset ) {
2010-03-17 01:06:19 +03:00
ret = e_snprintf ( offs , 32 , " +%lu " , pp - > offset ) ;
2009-12-01 03:20:17 +03:00
if ( ret < = 0 )
goto error ;
}
if ( pp - > line ) {
2010-03-17 01:06:19 +03:00
ret = e_snprintf ( line , 32 , " :%d " , pp - > line ) ;
if ( ret < = 0 )
goto error ;
}
if ( pp - > file ) {
2010-12-23 18:04:23 +03:00
tmp = pp - > file ;
len = strlen ( tmp ) ;
if ( len > 30 ) {
tmp = strchr ( pp - > file + len - 30 , ' / ' ) ;
tmp = tmp ? tmp + 1 : pp - > file + len - 30 ;
}
ret = e_snprintf ( file , 32 , " @%s " , tmp ) ;
2009-12-01 03:20:17 +03:00
if ( ret < = 0 )
goto error ;
}
if ( pp - > function )
2010-03-17 01:06:19 +03:00
ret = e_snprintf ( buf , MAX_CMDLEN , " %s%s%s%s%s " , pp - > function ,
offs , pp - > retprobe ? " %return " : " " , line ,
file ) ;
2009-12-01 03:20:17 +03:00
else
2010-03-17 01:06:19 +03:00
ret = e_snprintf ( buf , MAX_CMDLEN , " %s%s " , file , line ) ;
2010-03-17 01:06:12 +03:00
if ( ret < = 0 )
goto error ;
return buf ;
2009-12-15 18:32:47 +03:00
error :
2010-12-17 16:12:11 +03:00
pr_debug ( " Failed to synthesize perf probe point: %s \n " ,
2010-04-12 21:17:42 +04:00
strerror ( - ret ) ) ;
2010-04-12 21:17:49 +04:00
if ( buf )
free ( buf ) ;
2010-04-12 21:17:42 +04:00
return NULL ;
2009-12-15 18:32:47 +03:00
}
2010-03-17 01:06:12 +03:00
#if 0
char * synthesize_perf_probe_command ( struct perf_probe_event * pev )
2009-12-15 18:32:47 +03:00
{
char * buf ;
int i , len , ret ;
2010-03-17 01:06:12 +03:00
buf = synthesize_perf_probe_point ( & pev - > point ) ;
if ( ! buf )
return NULL ;
2009-12-01 03:20:17 +03:00
2010-03-17 01:06:12 +03:00
len = strlen ( buf ) ;
for ( i = 0 ; i < pev - > nargs ; i + + ) {
2009-12-01 03:20:17 +03:00
ret = e_snprintf ( & buf [ len ] , MAX_CMDLEN - len , " %s " ,
2010-03-17 01:06:12 +03:00
pev - > args [ i ] . name ) ;
if ( ret < = 0 ) {
free ( buf ) ;
return NULL ;
}
2009-12-01 03:20:17 +03:00
len + = ret ;
}
2010-03-17 01:06:12 +03:00
return buf ;
}
# endif
2010-07-29 18:13:51 +04:00
static int __synthesize_probe_trace_arg_ref ( struct probe_trace_arg_ref * ref ,
2010-03-17 01:06:12 +03:00
char * * buf , size_t * buflen ,
int depth )
{
int ret ;
if ( ref - > next ) {
2010-07-29 18:13:51 +04:00
depth = __synthesize_probe_trace_arg_ref ( ref - > next , buf ,
2010-03-17 01:06:12 +03:00
buflen , depth + 1 ) ;
if ( depth < 0 )
goto out ;
}
ret = e_snprintf ( * buf , * buflen , " %+ld( " , ref - > offset ) ;
if ( ret < 0 )
depth = ret ;
else {
* buf + = ret ;
* buflen - = ret ;
}
out :
return depth ;
2009-12-01 03:20:17 +03:00
}
2010-07-29 18:13:51 +04:00
static int synthesize_probe_trace_arg ( struct probe_trace_arg * arg ,
2010-03-17 01:06:12 +03:00
char * buf , size_t buflen )
2009-12-01 03:19:58 +03:00
{
2010-07-29 18:13:51 +04:00
struct probe_trace_arg_ref * ref = arg - > ref ;
2010-03-17 01:06:12 +03:00
int ret , depth = 0 ;
char * tmp = buf ;
/* Argument name or separator */
if ( arg - > name )
ret = e_snprintf ( buf , buflen , " %s= " , arg - > name ) ;
else
ret = e_snprintf ( buf , buflen , " " ) ;
if ( ret < 0 )
return ret ;
buf + = ret ;
buflen - = ret ;
2010-05-19 23:57:49 +04:00
/* Special case: @XXX */
if ( arg - > value [ 0 ] = = ' @ ' & & arg - > ref )
ref = ref - > next ;
2010-03-17 01:06:12 +03:00
/* Dereferencing arguments */
2010-05-19 23:57:49 +04:00
if ( ref ) {
2010-07-29 18:13:51 +04:00
depth = __synthesize_probe_trace_arg_ref ( ref , & buf ,
2010-03-17 01:06:12 +03:00
& buflen , 1 ) ;
if ( depth < 0 )
return depth ;
}
/* Print argument value */
2010-05-19 23:57:49 +04:00
if ( arg - > value [ 0 ] = = ' @ ' & & arg - > ref )
ret = e_snprintf ( buf , buflen , " %s%+ld " , arg - > value ,
arg - > ref - > offset ) ;
else
ret = e_snprintf ( buf , buflen , " %s " , arg - > value ) ;
2010-03-17 01:06:12 +03:00
if ( ret < 0 )
return ret ;
buf + = ret ;
buflen - = ret ;
/* Closing */
while ( depth - - ) {
ret = e_snprintf ( buf , buflen , " ) " ) ;
if ( ret < 0 )
return ret ;
buf + = ret ;
buflen - = ret ;
}
2010-04-12 21:17:15 +04:00
/* Print argument type */
if ( arg - > type ) {
ret = e_snprintf ( buf , buflen , " :%s " , arg - > type ) ;
if ( ret < = 0 )
return ret ;
buf + = ret ;
}
2010-03-17 01:06:12 +03:00
return buf - tmp ;
}
2010-07-29 18:13:51 +04:00
char * synthesize_probe_trace_command ( struct probe_trace_event * tev )
2010-03-17 01:06:12 +03:00
{
2010-07-29 18:13:51 +04:00
struct probe_trace_point * tp = & tev - > point ;
2009-12-01 03:19:58 +03:00
char * buf ;
int i , len , ret ;
2010-04-12 21:17:49 +04:00
buf = zalloc ( MAX_CMDLEN ) ;
if ( buf = = NULL )
return NULL ;
2011-06-27 11:27:45 +04:00
len = e_snprintf ( buf , MAX_CMDLEN , " %c:%s/%s %s%s%s+%lu " ,
2010-03-17 01:06:12 +03:00
tp - > retprobe ? ' r ' : ' p ' ,
tev - > group , tev - > event ,
2011-06-27 11:27:45 +04:00
tp - > module ? : " " , tp - > module ? " : " : " " ,
2010-03-17 01:06:12 +03:00
tp - > symbol , tp - > offset ) ;
if ( len < = 0 )
2009-12-01 03:19:58 +03:00
goto error ;
2010-03-17 01:06:12 +03:00
for ( i = 0 ; i < tev - > nargs ; i + + ) {
2010-07-29 18:13:51 +04:00
ret = synthesize_probe_trace_arg ( & tev - > args [ i ] , buf + len ,
2010-03-17 01:06:12 +03:00
MAX_CMDLEN - len ) ;
2009-12-01 03:20:17 +03:00
if ( ret < = 0 )
2009-12-01 03:19:58 +03:00
goto error ;
len + = ret ;
}
2010-03-17 01:06:12 +03:00
return buf ;
2009-12-01 03:19:58 +03:00
error :
2010-03-17 01:06:12 +03:00
free ( buf ) ;
return NULL ;
}
2009-12-01 03:19:58 +03:00
2010-07-29 18:13:51 +04:00
static int convert_to_perf_probe_event ( struct probe_trace_event * tev ,
2010-10-21 14:13:41 +04:00
struct perf_probe_event * pev )
2010-03-17 01:06:12 +03:00
{
2010-04-12 21:17:56 +04:00
char buf [ 64 ] = " " ;
2010-04-12 21:17:42 +04:00
int i , ret ;
2010-03-17 01:06:12 +03:00
2010-03-22 19:10:26 +03:00
/* Convert event/group name */
2010-04-12 21:17:56 +04:00
pev - > event = strdup ( tev - > event ) ;
pev - > group = strdup ( tev - > group ) ;
if ( pev - > event = = NULL | | pev - > group = = NULL )
return - ENOMEM ;
2010-03-17 01:06:19 +03:00
2010-03-22 19:10:26 +03:00
/* Convert trace_point to probe_point */
2010-07-29 18:13:51 +04:00
ret = kprobe_convert_to_perf_probe ( & tev - > point , & pev - > point ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
return ret ;
2010-03-22 19:10:26 +03:00
2010-03-17 01:06:12 +03:00
/* Convert trace_arg to probe_arg */
pev - > nargs = tev - > nargs ;
2010-04-12 21:17:49 +04:00
pev - > args = zalloc ( sizeof ( struct perf_probe_arg ) * pev - > nargs ) ;
if ( pev - > args = = NULL )
return - ENOMEM ;
2010-04-12 21:17:56 +04:00
for ( i = 0 ; i < tev - > nargs & & ret > = 0 ; i + + ) {
2010-03-17 01:06:12 +03:00
if ( tev - > args [ i ] . name )
2010-04-12 21:17:56 +04:00
pev - > args [ i ] . name = strdup ( tev - > args [ i ] . name ) ;
2010-03-17 01:06:12 +03:00
else {
2010-07-29 18:13:51 +04:00
ret = synthesize_probe_trace_arg ( & tev - > args [ i ] ,
2010-04-12 21:17:42 +04:00
buf , 64 ) ;
2010-04-12 21:17:56 +04:00
pev - > args [ i ] . name = strdup ( buf ) ;
2010-03-17 01:06:12 +03:00
}
2010-04-12 21:17:56 +04:00
if ( pev - > args [ i ] . name = = NULL & & ret > = 0 )
ret = - ENOMEM ;
}
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
clear_perf_probe_event ( pev ) ;
return ret ;
2010-03-17 01:06:12 +03:00
}
void clear_perf_probe_event ( struct perf_probe_event * pev )
{
struct perf_probe_point * pp = & pev - > point ;
2010-03-17 01:06:26 +03:00
struct perf_probe_arg_field * field , * next ;
2010-03-17 01:06:12 +03:00
int i ;
if ( pev - > event )
free ( pev - > event ) ;
if ( pev - > group )
free ( pev - > group ) ;
if ( pp - > file )
free ( pp - > file ) ;
if ( pp - > function )
free ( pp - > function ) ;
if ( pp - > lazy_line )
free ( pp - > lazy_line ) ;
2010-03-17 01:06:26 +03:00
for ( i = 0 ; i < pev - > nargs ; i + + ) {
2010-03-17 01:06:12 +03:00
if ( pev - > args [ i ] . name )
free ( pev - > args [ i ] . name ) ;
2010-04-12 21:16:53 +04:00
if ( pev - > args [ i ] . var )
free ( pev - > args [ i ] . var ) ;
2010-04-12 21:17:22 +04:00
if ( pev - > args [ i ] . type )
free ( pev - > args [ i ] . type ) ;
2010-03-17 01:06:26 +03:00
field = pev - > args [ i ] . field ;
while ( field ) {
next = field - > next ;
if ( field - > name )
free ( field - > name ) ;
free ( field ) ;
field = next ;
}
}
2010-03-17 01:06:12 +03:00
if ( pev - > args )
free ( pev - > args ) ;
memset ( pev , 0 , sizeof ( * pev ) ) ;
}
2010-07-29 18:13:51 +04:00
static void clear_probe_trace_event ( struct probe_trace_event * tev )
2010-03-17 01:06:12 +03:00
{
2010-07-29 18:13:51 +04:00
struct probe_trace_arg_ref * ref , * next ;
2010-03-17 01:06:12 +03:00
int i ;
if ( tev - > event )
free ( tev - > event ) ;
if ( tev - > group )
free ( tev - > group ) ;
if ( tev - > point . symbol )
free ( tev - > point . symbol ) ;
2011-06-27 11:27:45 +04:00
if ( tev - > point . module )
free ( tev - > point . module ) ;
2010-03-17 01:06:12 +03:00
for ( i = 0 ; i < tev - > nargs ; i + + ) {
if ( tev - > args [ i ] . name )
free ( tev - > args [ i ] . name ) ;
if ( tev - > args [ i ] . value )
free ( tev - > args [ i ] . value ) ;
2010-04-12 21:17:15 +04:00
if ( tev - > args [ i ] . type )
free ( tev - > args [ i ] . type ) ;
2010-03-17 01:06:12 +03:00
ref = tev - > args [ i ] . ref ;
while ( ref ) {
next = ref - > next ;
free ( ref ) ;
ref = next ;
}
}
if ( tev - > args )
free ( tev - > args ) ;
memset ( tev , 0 , sizeof ( * tev ) ) ;
2009-12-01 03:19:58 +03:00
}
2010-03-17 01:06:05 +03:00
static int open_kprobe_events ( bool readwrite )
2009-12-01 03:20:17 +03:00
{
char buf [ PATH_MAX ] ;
2010-04-15 02:39:28 +04:00
const char * __debugfs ;
2009-12-01 03:20:17 +03:00
int ret ;
2010-04-15 02:39:28 +04:00
__debugfs = debugfs_find_mountpoint ( ) ;
if ( __debugfs = = NULL ) {
pr_warning ( " Debugfs is not mounted. \n " ) ;
return - ENOENT ;
}
ret = e_snprintf ( buf , PATH_MAX , " %stracing/kprobe_events " , __debugfs ) ;
2010-04-12 21:17:42 +04:00
if ( ret > = 0 ) {
2010-04-15 02:39:28 +04:00
pr_debug ( " Opening %s write=%d \n " , buf , readwrite ) ;
2010-04-12 21:17:42 +04:00
if ( readwrite & & ! probe_event_dry_run )
ret = open ( buf , O_RDWR , O_APPEND ) ;
else
ret = open ( buf , O_RDONLY , 0 ) ;
}
2010-03-17 01:06:05 +03:00
2009-12-01 03:20:17 +03:00
if ( ret < 0 ) {
if ( errno = = ENOENT )
2010-04-12 21:17:42 +04:00
pr_warning ( " kprobe_events file does not exist - please "
" rebuild kernel with CONFIG_KPROBE_EVENT. \n " ) ;
2009-12-01 03:20:17 +03:00
else
2010-04-12 21:17:42 +04:00
pr_warning ( " Failed to open kprobe_events file: %s \n " ,
strerror ( errno ) ) ;
2009-12-01 03:20:17 +03:00
}
return ret ;
}
/* Get raw string list of current kprobe_events */
2010-07-29 18:13:51 +04:00
static struct strlist * get_probe_trace_command_rawlist ( int fd )
2009-12-01 03:20:17 +03:00
{
int ret , idx ;
FILE * fp ;
char buf [ MAX_CMDLEN ] ;
char * p ;
struct strlist * sl ;
sl = strlist__new ( true , NULL ) ;
fp = fdopen ( dup ( fd ) , " r " ) ;
while ( ! feof ( fp ) ) {
p = fgets ( buf , MAX_CMDLEN , fp ) ;
if ( ! p )
break ;
idx = strlen ( p ) - 1 ;
if ( p [ idx ] = = ' \n ' )
p [ idx ] = ' \0 ' ;
ret = strlist__add ( sl , buf ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 ) {
pr_debug ( " strlist__add failed: %s \n " , strerror ( - ret ) ) ;
strlist__delete ( sl ) ;
return NULL ;
}
2009-12-01 03:20:17 +03:00
}
fclose ( fp ) ;
return sl ;
}
2009-12-09 01:02:40 +03:00
/* Show an event */
2010-04-12 21:17:42 +04:00
static int show_perf_probe_event ( struct perf_probe_event * pev )
2009-12-09 01:02:40 +03:00
{
2009-12-15 18:31:21 +03:00
int i , ret ;
2009-12-09 01:02:40 +03:00
char buf [ 128 ] ;
2010-03-17 01:06:12 +03:00
char * place ;
2009-12-09 01:02:40 +03:00
2010-03-17 01:06:12 +03:00
/* Synthesize only event probe point */
place = synthesize_perf_probe_point ( & pev - > point ) ;
2010-04-12 21:17:42 +04:00
if ( ! place )
return - EINVAL ;
2010-03-17 01:06:12 +03:00
ret = e_snprintf ( buf , 128 , " %s:%s " , pev - > group , pev - > event ) ;
2009-12-15 18:31:21 +03:00
if ( ret < 0 )
2010-04-12 21:17:42 +04:00
return ret ;
2010-03-17 01:06:19 +03:00
printf ( " %-20s (on %s " , buf , place ) ;
2009-12-09 01:02:40 +03:00
2010-03-17 01:06:12 +03:00
if ( pev - > nargs > 0 ) {
2009-12-09 01:02:40 +03:00
printf ( " with " ) ;
2010-03-17 01:06:26 +03:00
for ( i = 0 ; i < pev - > nargs ; i + + ) {
2010-04-12 21:17:42 +04:00
ret = synthesize_perf_probe_arg ( & pev - > args [ i ] ,
buf , 128 ) ;
if ( ret < 0 )
break ;
2010-03-17 01:06:26 +03:00
printf ( " %s " , buf ) ;
}
2009-12-09 01:02:40 +03:00
}
printf ( " ) \n " ) ;
2010-03-17 01:06:12 +03:00
free ( place ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2009-12-09 01:02:40 +03:00
}
2009-12-01 03:20:17 +03:00
/* List up current perf-probe events */
2010-04-12 21:17:42 +04:00
int show_perf_probe_events ( void )
2009-12-01 03:20:17 +03:00
{
2010-04-12 21:17:42 +04:00
int fd , ret ;
2010-07-29 18:13:51 +04:00
struct probe_trace_event tev ;
2010-03-17 01:06:12 +03:00
struct perf_probe_event pev ;
2009-12-01 03:20:17 +03:00
struct strlist * rawlist ;
struct str_node * ent ;
2010-01-06 01:47:10 +03:00
setup_pager ( ) ;
2010-04-12 21:17:42 +04:00
ret = init_vmlinux ( ) ;
if ( ret < 0 )
return ret ;
2010-03-17 01:06:12 +03:00
memset ( & tev , 0 , sizeof ( tev ) ) ;
memset ( & pev , 0 , sizeof ( pev ) ) ;
2010-01-06 01:47:10 +03:00
2010-03-17 01:06:05 +03:00
fd = open_kprobe_events ( false ) ;
2010-04-12 21:17:42 +04:00
if ( fd < 0 )
return fd ;
2010-07-29 18:13:51 +04:00
rawlist = get_probe_trace_command_rawlist ( fd ) ;
2009-12-01 03:20:17 +03:00
close ( fd ) ;
2010-04-12 21:17:42 +04:00
if ( ! rawlist )
return - ENOENT ;
2009-12-01 03:20:17 +03:00
2009-12-15 18:32:03 +03:00
strlist__for_each ( ent , rawlist ) {
2010-07-29 18:13:51 +04:00
ret = parse_probe_trace_command ( ent - > s , & tev ) ;
2010-04-12 21:17:42 +04:00
if ( ret > = 0 ) {
ret = convert_to_perf_probe_event ( & tev , & pev ) ;
if ( ret > = 0 )
ret = show_perf_probe_event ( & pev ) ;
}
2010-03-17 01:06:12 +03:00
clear_perf_probe_event ( & pev ) ;
2010-07-29 18:13:51 +04:00
clear_probe_trace_event ( & tev ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
break ;
2009-12-01 03:20:17 +03:00
}
strlist__delete ( rawlist ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2009-12-01 03:20:17 +03:00
}
2009-12-01 03:20:25 +03:00
/* Get current perf-probe event names */
2010-07-29 18:13:51 +04:00
static struct strlist * get_probe_trace_event_names ( int fd , bool include_group )
2009-12-01 03:20:25 +03:00
{
2009-12-09 01:03:23 +03:00
char buf [ 128 ] ;
2009-12-01 03:20:25 +03:00
struct strlist * sl , * rawlist ;
struct str_node * ent ;
2010-07-29 18:13:51 +04:00
struct probe_trace_event tev ;
2010-04-12 21:17:42 +04:00
int ret = 0 ;
2009-12-01 03:20:25 +03:00
2010-03-17 01:06:12 +03:00
memset ( & tev , 0 , sizeof ( tev ) ) ;
2010-07-29 18:13:51 +04:00
rawlist = get_probe_trace_command_rawlist ( fd ) ;
2009-12-07 20:00:46 +03:00
sl = strlist__new ( true , NULL ) ;
2009-12-15 18:32:03 +03:00
strlist__for_each ( ent , rawlist ) {
2010-07-29 18:13:51 +04:00
ret = parse_probe_trace_command ( ent - > s , & tev ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
break ;
2009-12-09 01:03:23 +03:00
if ( include_group ) {
2010-04-12 21:17:42 +04:00
ret = e_snprintf ( buf , 128 , " %s:%s " , tev . group ,
tev . event ) ;
if ( ret > = 0 )
ret = strlist__add ( sl , buf ) ;
2009-12-09 01:03:23 +03:00
} else
2010-04-12 21:17:42 +04:00
ret = strlist__add ( sl , tev . event ) ;
2010-07-29 18:13:51 +04:00
clear_probe_trace_event ( & tev ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
break ;
2009-12-01 03:20:25 +03:00
}
strlist__delete ( rawlist ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 ) {
strlist__delete ( sl ) ;
return NULL ;
}
2009-12-01 03:20:25 +03:00
return sl ;
}
2010-07-29 18:13:51 +04:00
static int write_probe_trace_event ( int fd , struct probe_trace_event * tev )
2009-12-01 03:19:58 +03:00
{
2010-04-21 04:01:05 +04:00
int ret = 0 ;
2010-07-29 18:13:51 +04:00
char * buf = synthesize_probe_trace_command ( tev ) ;
2009-12-01 03:19:58 +03:00
2010-04-12 21:17:42 +04:00
if ( ! buf ) {
2010-07-29 18:13:51 +04:00
pr_debug ( " Failed to synthesize probe trace event. \n " ) ;
2010-04-12 21:17:42 +04:00
return - EINVAL ;
}
2009-12-09 01:03:23 +03:00
pr_debug ( " Writing event: %s \n " , buf ) ;
2010-03-17 01:06:05 +03:00
if ( ! probe_event_dry_run ) {
ret = write ( fd , buf , strlen ( buf ) ) ;
if ( ret < = 0 )
2010-04-12 21:17:42 +04:00
pr_warning ( " Failed to write event: %s \n " ,
strerror ( errno ) ) ;
2010-03-17 01:06:05 +03:00
}
2010-03-17 01:06:12 +03:00
free ( buf ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2009-12-01 03:19:58 +03:00
}
2010-04-12 21:17:42 +04:00
static int get_new_event_name ( char * buf , size_t len , const char * base ,
struct strlist * namelist , bool allow_suffix )
2009-12-01 03:20:25 +03:00
{
int i , ret ;
2009-12-09 01:03:02 +03:00
/* Try no suffix */
ret = e_snprintf ( buf , len , " %s " , base ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 ) {
pr_debug ( " snprintf() failed: %s \n " , strerror ( - ret ) ) ;
return ret ;
}
2009-12-09 01:03:02 +03:00
if ( ! strlist__has_entry ( namelist , buf ) )
2010-04-12 21:17:42 +04:00
return 0 ;
2009-12-09 01:03:02 +03:00
2009-12-15 18:32:25 +03:00
if ( ! allow_suffix ) {
pr_warning ( " Error: event \" %s \" already exists. "
" (Use -f to force duplicates.) \n " , base ) ;
2010-04-12 21:17:42 +04:00
return - EEXIST ;
2009-12-15 18:32:25 +03:00
}
2009-12-09 01:03:02 +03:00
/* Try to add suffix */
for ( i = 1 ; i < MAX_EVENT_INDEX ; i + + ) {
2009-12-01 03:20:25 +03:00
ret = e_snprintf ( buf , len , " %s_%d " , base , i ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 ) {
pr_debug ( " snprintf() failed: %s \n " , strerror ( - ret ) ) ;
return ret ;
}
2009-12-01 03:20:25 +03:00
if ( ! strlist__has_entry ( namelist , buf ) )
break ;
}
2010-04-12 21:17:42 +04:00
if ( i = = MAX_EVENT_INDEX ) {
pr_warning ( " Too many events are on the same function. \n " ) ;
ret = - ERANGE ;
}
return ret ;
2009-12-01 03:20:25 +03:00
}
2010-07-29 18:13:51 +04:00
static int __add_probe_trace_events ( struct perf_probe_event * pev ,
struct probe_trace_event * tevs ,
2010-04-12 21:17:42 +04:00
int ntevs , bool allow_suffix )
2009-12-01 03:19:58 +03:00
{
2010-04-12 21:17:42 +04:00
int i , fd , ret ;
2010-07-29 18:13:51 +04:00
struct probe_trace_event * tev = NULL ;
2010-03-17 01:06:12 +03:00
char buf [ 64 ] ;
const char * event , * group ;
2009-12-01 03:20:25 +03:00
struct strlist * namelist ;
2009-12-01 03:19:58 +03:00
2010-03-17 01:06:05 +03:00
fd = open_kprobe_events ( true ) ;
2010-04-12 21:17:42 +04:00
if ( fd < 0 )
return fd ;
2009-12-01 03:20:25 +03:00
/* Get current event names */
2010-07-29 18:13:51 +04:00
namelist = get_probe_trace_event_names ( fd , false ) ;
2010-04-12 21:17:42 +04:00
if ( ! namelist ) {
pr_debug ( " Failed to get current event list. \n " ) ;
return - EIO ;
}
2010-03-17 01:06:12 +03:00
2010-04-12 21:17:42 +04:00
ret = 0 ;
2010-03-17 01:06:12 +03:00
printf ( " Add new event%s \n " , ( ntevs > 1 ) ? " s: " : " : " ) ;
2010-04-12 21:17:56 +04:00
for ( i = 0 ; i < ntevs ; i + + ) {
2010-03-17 01:06:12 +03:00
tev = & tevs [ i ] ;
if ( pev - > event )
event = pev - > event ;
else
if ( pev - > point . function )
event = pev - > point . function ;
else
event = tev - > point . symbol ;
if ( pev - > group )
group = pev - > group ;
else
group = PERFPROBE_GROUP ;
/* Get an unused new event name */
2010-04-12 21:17:42 +04:00
ret = get_new_event_name ( buf , 64 , event ,
namelist , allow_suffix ) ;
if ( ret < 0 )
break ;
2010-03-17 01:06:12 +03:00
event = buf ;
2010-04-12 21:17:56 +04:00
tev - > event = strdup ( event ) ;
tev - > group = strdup ( group ) ;
if ( tev - > event = = NULL | | tev - > group = = NULL ) {
ret = - ENOMEM ;
break ;
}
2010-07-29 18:13:51 +04:00
ret = write_probe_trace_event ( fd , tev ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
break ;
2010-03-17 01:06:12 +03:00
/* Add added event name to namelist */
strlist__add ( namelist , event ) ;
/* Trick here - save current event/group */
event = pev - > event ;
group = pev - > group ;
pev - > event = tev - > event ;
pev - > group = tev - > group ;
show_perf_probe_event ( pev ) ;
/* Trick here - restore current event/group */
pev - > event = ( char * ) event ;
pev - > group = ( char * ) group ;
/*
* Probes after the first probe which comes from same
* user input are always allowed to add suffix , because
* there might be several addresses corresponding to
* one code line .
*/
allow_suffix = true ;
2009-12-01 03:19:58 +03:00
}
2010-04-12 21:17:42 +04:00
if ( ret > = 0 ) {
/* Show how to use the event. */
printf ( " \n You can now use it on all perf tools, such as: \n \n " ) ;
printf ( " \t perf record -e %s:%s -aR sleep 1 \n \n " , tev - > group ,
tev - > event ) ;
}
2009-12-09 01:02:47 +03:00
2009-12-07 20:00:46 +03:00
strlist__delete ( namelist ) ;
2009-12-01 03:19:58 +03:00
close ( fd ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2009-12-01 03:19:58 +03:00
}
2009-12-09 01:03:23 +03:00
2010-07-29 18:13:51 +04:00
static int convert_to_probe_trace_events ( struct perf_probe_event * pev ,
struct probe_trace_event * * tevs ,
2010-10-21 14:13:41 +04:00
int max_tevs , const char * module )
2010-03-17 01:05:37 +03:00
{
struct symbol * sym ;
2010-04-12 21:17:49 +04:00
int ret = 0 , i ;
2010-07-29 18:13:51 +04:00
struct probe_trace_event * tev ;
2010-03-17 01:06:12 +03:00
2010-03-22 19:10:26 +03:00
/* Convert perf_probe_event with debuginfo */
2010-10-21 14:13:41 +04:00
ret = try_to_find_probe_trace_events ( pev , tevs , max_tevs , module ) ;
2010-04-12 21:17:49 +04:00
if ( ret ! = 0 )
2011-06-27 11:27:45 +04:00
return ret ; /* Found in debuginfo or got an error */
2010-03-17 01:05:37 +03:00
2010-03-17 01:06:12 +03:00
/* Allocate trace event buffer */
2010-07-29 18:13:51 +04:00
tev = * tevs = zalloc ( sizeof ( struct probe_trace_event ) ) ;
2010-04-12 21:17:49 +04:00
if ( tev = = NULL )
return - ENOMEM ;
2010-03-17 01:06:12 +03:00
/* Copy parameters */
2010-04-12 21:17:56 +04:00
tev - > point . symbol = strdup ( pev - > point . function ) ;
if ( tev - > point . symbol = = NULL ) {
ret = - ENOMEM ;
goto error ;
}
2011-07-25 18:08:08 +04:00
if ( module ) {
tev - > point . module = strdup ( module ) ;
if ( tev - > point . module = = NULL ) {
ret = - ENOMEM ;
goto error ;
}
2011-06-27 11:27:45 +04:00
}
2011-07-25 18:08:08 +04:00
2010-03-17 01:06:12 +03:00
tev - > point . offset = pev - > point . offset ;
2010-08-27 15:38:53 +04:00
tev - > point . retprobe = pev - > point . retprobe ;
2010-03-17 01:06:12 +03:00
tev - > nargs = pev - > nargs ;
if ( tev - > nargs ) {
2010-07-29 18:13:51 +04:00
tev - > args = zalloc ( sizeof ( struct probe_trace_arg )
2010-04-12 21:17:49 +04:00
* tev - > nargs ) ;
if ( tev - > args = = NULL ) {
2010-04-12 21:17:56 +04:00
ret = - ENOMEM ;
goto error ;
2010-04-12 21:17:49 +04:00
}
2010-04-12 21:16:53 +04:00
for ( i = 0 ; i < tev - > nargs ; i + + ) {
2010-04-12 21:17:56 +04:00
if ( pev - > args [ i ] . name ) {
tev - > args [ i ] . name = strdup ( pev - > args [ i ] . name ) ;
if ( tev - > args [ i ] . name = = NULL ) {
ret = - ENOMEM ;
goto error ;
}
}
tev - > args [ i ] . value = strdup ( pev - > args [ i ] . var ) ;
if ( tev - > args [ i ] . value = = NULL ) {
ret = - ENOMEM ;
goto error ;
}
if ( pev - > args [ i ] . type ) {
tev - > args [ i ] . type = strdup ( pev - > args [ i ] . type ) ;
if ( tev - > args [ i ] . type = = NULL ) {
ret = - ENOMEM ;
goto error ;
}
}
2010-04-12 21:16:53 +04:00
}
2010-03-17 01:06:12 +03:00
}
/* Currently just checking function name from symbol map */
2010-10-21 14:13:41 +04:00
sym = __find_kernel_function_by_name ( tev - > point . symbol , NULL ) ;
2010-04-12 21:17:42 +04:00
if ( ! sym ) {
pr_warning ( " Kernel symbol \' %s \' not found. \n " ,
tev - > point . symbol ) ;
2010-04-12 21:17:56 +04:00
ret = - ENOENT ;
goto error ;
}
2010-04-12 21:17:49 +04:00
2010-04-12 21:17:56 +04:00
return 1 ;
error :
2010-07-29 18:13:51 +04:00
clear_probe_trace_event ( tev ) ;
2010-04-12 21:17:56 +04:00
free ( tev ) ;
* tevs = NULL ;
2010-04-12 21:17:49 +04:00
return ret ;
2010-03-17 01:06:12 +03:00
}
struct __event_package {
struct perf_probe_event * pev ;
2010-07-29 18:13:51 +04:00
struct probe_trace_event * tevs ;
2010-03-17 01:06:12 +03:00
int ntevs ;
} ;
2010-04-12 21:17:42 +04:00
int add_perf_probe_events ( struct perf_probe_event * pevs , int npevs ,
2010-10-21 14:13:41 +04:00
int max_tevs , const char * module , bool force_add )
2010-03-17 01:06:12 +03:00
{
2010-04-12 21:17:42 +04:00
int i , j , ret ;
2010-03-17 01:06:12 +03:00
struct __event_package * pkgs ;
2010-04-12 21:17:49 +04:00
pkgs = zalloc ( sizeof ( struct __event_package ) * npevs ) ;
if ( pkgs = = NULL )
return - ENOMEM ;
2010-03-17 01:06:12 +03:00
/* Init vmlinux path */
2010-04-12 21:17:42 +04:00
ret = init_vmlinux ( ) ;
2010-08-03 06:11:40 +04:00
if ( ret < 0 ) {
free ( pkgs ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2010-08-03 06:11:40 +04:00
}
2010-03-17 01:06:12 +03:00
/* Loop 1: convert all events */
for ( i = 0 ; i < npevs ; i + + ) {
pkgs [ i ] . pev = & pevs [ i ] ;
/* Convert with or without debuginfo */
2010-07-29 18:13:51 +04:00
ret = convert_to_probe_trace_events ( pkgs [ i ] . pev ,
2010-10-21 14:13:41 +04:00
& pkgs [ i ] . tevs ,
max_tevs ,
module ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
goto end ;
pkgs [ i ] . ntevs = ret ;
2010-03-17 01:05:37 +03:00
}
2010-03-17 01:06:12 +03:00
/* Loop 2: add all events */
2011-02-22 12:56:18 +03:00
for ( i = 0 ; i < npevs ; i + + ) {
2010-07-29 18:13:51 +04:00
ret = __add_probe_trace_events ( pkgs [ i ] . pev , pkgs [ i ] . tevs ,
2010-04-12 21:17:42 +04:00
pkgs [ i ] . ntevs , force_add ) ;
2011-02-21 19:23:57 +03:00
if ( ret < 0 )
break ;
}
2010-04-12 21:17:42 +04:00
end :
2010-08-03 06:11:40 +04:00
/* Loop 3: cleanup and free trace events */
for ( i = 0 ; i < npevs ; i + + ) {
2010-04-12 21:17:42 +04:00
for ( j = 0 ; j < pkgs [ i ] . ntevs ; j + + )
2010-07-29 18:13:51 +04:00
clear_probe_trace_event ( & pkgs [ i ] . tevs [ j ] ) ;
2010-08-03 06:11:40 +04:00
free ( pkgs [ i ] . tevs ) ;
}
free ( pkgs ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2010-03-17 01:05:37 +03:00
}
2010-07-29 18:13:51 +04:00
static int __del_trace_probe_event ( int fd , struct str_node * ent )
2009-12-15 18:32:10 +03:00
{
char * p ;
char buf [ 128 ] ;
2010-03-17 01:06:12 +03:00
int ret ;
2009-12-15 18:32:10 +03:00
2010-07-29 18:13:51 +04:00
/* Convert from perf-probe event to trace-probe event */
2010-04-12 21:17:42 +04:00
ret = e_snprintf ( buf , 128 , " -:%s " , ent - > s ) ;
if ( ret < 0 )
goto error ;
2009-12-15 18:32:10 +03:00
p = strchr ( buf + 2 , ' : ' ) ;
2010-04-12 21:17:42 +04:00
if ( ! p ) {
pr_debug ( " Internal error: %s should have ':' but not. \n " ,
ent - > s ) ;
ret = - ENOTSUP ;
goto error ;
}
2009-12-15 18:32:10 +03:00
* p = ' / ' ;
2010-03-17 01:06:12 +03:00
pr_debug ( " Writing event: %s \n " , buf ) ;
ret = write ( fd , buf , strlen ( buf ) ) ;
2011-10-04 14:45:04 +04:00
if ( ret < 0 ) {
ret = - errno ;
2010-04-12 21:17:42 +04:00
goto error ;
2011-10-04 14:45:04 +04:00
}
2010-04-12 21:17:42 +04:00
2009-12-15 18:32:10 +03:00
printf ( " Remove event: %s \n " , ent - > s ) ;
2010-04-12 21:17:42 +04:00
return 0 ;
error :
pr_warning ( " Failed to delete event: %s \n " , strerror ( - ret ) ) ;
return ret ;
2009-12-15 18:32:10 +03:00
}
2010-07-29 18:13:51 +04:00
static int del_trace_probe_event ( int fd , const char * group ,
2010-04-12 21:17:42 +04:00
const char * event , struct strlist * namelist )
2009-12-09 01:03:23 +03:00
{
char buf [ 128 ] ;
2009-12-15 18:32:10 +03:00
struct str_node * ent , * n ;
2010-04-12 21:17:42 +04:00
int found = 0 , ret = 0 ;
2009-12-09 01:03:23 +03:00
2010-04-12 21:17:42 +04:00
ret = e_snprintf ( buf , 128 , " %s:%s " , group , event ) ;
if ( ret < 0 ) {
2010-12-17 16:12:11 +03:00
pr_err ( " Failed to copy event. \n " ) ;
2010-04-12 21:17:42 +04:00
return ret ;
}
2009-12-09 01:03:23 +03:00
2009-12-15 18:32:10 +03:00
if ( strpbrk ( buf , " *? " ) ) { /* Glob-exp */
strlist__for_each_safe ( ent , n , namelist )
if ( strglobmatch ( ent - > s , buf ) ) {
found + + ;
2010-07-29 18:13:51 +04:00
ret = __del_trace_probe_event ( fd , ent ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
break ;
2009-12-15 18:32:10 +03:00
strlist__remove ( namelist , ent ) ;
}
} else {
ent = strlist__find ( namelist , buf ) ;
if ( ent ) {
found + + ;
2010-07-29 18:13:51 +04:00
ret = __del_trace_probe_event ( fd , ent ) ;
2010-04-12 21:17:42 +04:00
if ( ret > = 0 )
strlist__remove ( namelist , ent ) ;
2009-12-15 18:32:10 +03:00
}
}
2010-04-12 21:17:42 +04:00
if ( found = = 0 & & ret > = 0 )
pr_info ( " Info: Event \" %s \" does not exist. \n " , buf ) ;
return ret ;
2009-12-09 01:03:23 +03:00
}
2010-04-12 21:17:42 +04:00
int del_perf_probe_events ( struct strlist * dellist )
2009-12-09 01:03:23 +03:00
{
2010-04-12 21:17:42 +04:00
int fd , ret = 0 ;
2009-12-09 01:03:23 +03:00
const char * group , * event ;
char * p , * str ;
struct str_node * ent ;
struct strlist * namelist ;
2010-03-17 01:06:05 +03:00
fd = open_kprobe_events ( true ) ;
2010-04-12 21:17:42 +04:00
if ( fd < 0 )
return fd ;
2009-12-09 01:03:23 +03:00
/* Get current event names */
2010-07-29 18:13:51 +04:00
namelist = get_probe_trace_event_names ( fd , true ) ;
2010-04-12 21:17:42 +04:00
if ( namelist = = NULL )
return - EINVAL ;
2009-12-09 01:03:23 +03:00
2009-12-15 18:32:03 +03:00
strlist__for_each ( ent , dellist ) {
2010-04-12 21:17:56 +04:00
str = strdup ( ent - > s ) ;
if ( str = = NULL ) {
ret = - ENOMEM ;
break ;
}
2009-12-15 18:32:10 +03:00
pr_debug ( " Parsing: %s \n " , str ) ;
2009-12-09 01:03:23 +03:00
p = strchr ( str , ' : ' ) ;
if ( p ) {
group = str ;
* p = ' \0 ' ;
event = p + 1 ;
} else {
2009-12-15 18:32:10 +03:00
group = " * " ;
2009-12-09 01:03:23 +03:00
event = str ;
}
2009-12-15 18:32:10 +03:00
pr_debug ( " Group: %s, Event: %s \n " , group , event ) ;
2010-07-29 18:13:51 +04:00
ret = del_trace_probe_event ( fd , group , event , namelist ) ;
2009-12-09 01:03:23 +03:00
free ( str ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
break ;
2009-12-09 01:03:23 +03:00
}
strlist__delete ( namelist ) ;
close ( fd ) ;
2010-04-12 21:17:42 +04:00
return ret ;
2009-12-09 01:03:23 +03:00
}
2011-01-20 17:15:45 +03:00
/* TODO: don't use a global variable for filter ... */
static struct strfilter * available_func_filter ;
2009-12-09 01:03:23 +03:00
2011-01-13 15:46:11 +03:00
/*
2011-01-20 17:15:45 +03:00
* If a symbol corresponds to a function with global binding and
* matches filter return 0. For all others return 1.
2011-01-13 15:46:11 +03:00
*/
2011-01-20 17:15:45 +03:00
static int filter_available_functions ( struct map * map __unused ,
struct symbol * sym )
2011-01-13 15:46:11 +03:00
{
2011-01-20 17:15:45 +03:00
if ( sym - > binding = = STB_GLOBAL & &
strfilter__compare ( available_func_filter , sym - > name ) )
return 0 ;
return 1 ;
2011-01-13 15:46:11 +03:00
}
2011-01-20 17:15:45 +03:00
int show_available_funcs ( const char * module , struct strfilter * _filter )
2011-01-13 15:46:11 +03:00
{
struct map * map ;
int ret ;
setup_pager ( ) ;
ret = init_vmlinux ( ) ;
if ( ret < 0 )
return ret ;
map = kernel_get_module_map ( module ) ;
if ( ! map ) {
pr_err ( " Failed to find %s map. \n " , ( module ) ? : " kernel " ) ;
return - EINVAL ;
}
2011-01-20 17:15:45 +03:00
available_func_filter = _filter ;
if ( map__load ( map , filter_available_functions ) ) {
2011-01-13 15:46:11 +03:00
pr_err ( " Failed to load map. \n " ) ;
return - EINVAL ;
}
if ( ! dso__sorted_by_name ( map - > dso , map - > type ) )
dso__sort_by_name ( map - > dso , map - > type ) ;
dso__fprintf_symbols_by_name ( map - > dso , map - > type , stdout ) ;
return 0 ;
}