2009-12-01 03:19:58 +03:00
/*
* probe - event . c : perf - probe definition to kprobe_events format converter
*
* 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>
2009-12-01 03:20:17 +03:00
# include <stdarg.h>
# include <limits.h>
2009-12-01 03:19:58 +03:00
# undef _GNU_SOURCE
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-03-22 19:10:26 +03:00
/* Initialize symbol maps and path of vmlinux */
2010-04-12 21:17:42 +04:00
static int init_vmlinux ( void )
2010-03-17 01:05:37 +03:00
{
2010-04-19 09:32:50 +04:00
struct dso * kernel ;
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-04-28 04:20:43 +04:00
ret = machine__init ( & machine , " / " , 0 ) ;
if ( ret < 0 )
goto out ;
2010-04-19 09:32:50 +04:00
kernel = dso__new_kernel ( symbol_conf . vmlinux_name ) ;
if ( kernel = = NULL )
die ( " Failed to create kernel dso. " ) ;
2010-04-28 04:20:43 +04:00
ret = __machine__create_kernel_maps ( & machine , kernel ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
pr_debug ( " Failed to create kernel maps. \n " ) ;
out :
if ( ret < 0 )
pr_warning ( " Failed to init vmlinux path. \n " ) ;
return ret ;
2010-03-17 01:05:37 +03:00
}
2010-03-22 19:10:26 +03:00
# ifdef DWARF_SUPPORT
2010-03-17 01:05:37 +03:00
static int open_vmlinux ( void )
{
2010-04-28 04:20:43 +04:00
if ( map__load ( machine . vmlinux_maps [ MAP__FUNCTION ] , NULL ) < 0 ) {
2010-03-17 01:05:37 +03:00
pr_debug ( " Failed to load kernel map. \n " ) ;
return - EINVAL ;
}
2010-04-28 04:20:43 +04:00
pr_debug ( " Try to open %s \n " , machine . vmlinux_maps [ MAP__FUNCTION ] - > dso - > long_name ) ;
return open ( machine . vmlinux_maps [ MAP__FUNCTION ] - > dso - > long_name , O_RDONLY ) ;
2010-03-17 01:05:37 +03:00
}
2010-03-22 19:10:26 +03:00
2010-04-12 21:17:42 +04:00
/* Convert trace point to probe point with debuginfo */
static int convert_to_perf_probe_point ( struct kprobe_trace_point * tp ,
struct perf_probe_point * pp )
2010-03-22 19:10:26 +03:00
{
struct symbol * sym ;
2010-04-12 21:17:42 +04:00
int fd , ret = - ENOENT ;
2010-03-22 19:10:26 +03:00
2010-04-28 04:20:43 +04:00
sym = map__find_symbol_by_name ( machine . vmlinux_maps [ MAP__FUNCTION ] ,
2010-03-22 19:10:26 +03:00
tp - > symbol , NULL ) ;
if ( sym ) {
fd = open_vmlinux ( ) ;
2010-04-12 21:17:42 +04:00
if ( fd > = 0 ) {
ret = find_perf_probe_point ( fd ,
sym - > start + tp - > offset , pp ) ;
close ( fd ) ;
}
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
}
/* Try to find perf_probe_event with debuginfo */
static int try_to_find_kprobe_trace_events ( struct perf_probe_event * pev ,
2010-04-21 23:56:40 +04:00
struct kprobe_trace_event * * tevs ,
int max_tevs )
2010-03-22 19:10:26 +03:00
{
bool need_dwarf = perf_probe_event_need_dwarf ( pev ) ;
int fd , ntevs ;
fd = open_vmlinux ( ) ;
if ( fd < 0 ) {
2010-04-12 21:17:42 +04:00
if ( need_dwarf ) {
pr_warning ( " Failed to open debuginfo file. \n " ) ;
return fd ;
}
2010-03-22 19:10:26 +03:00
pr_debug ( " Could not open vmlinux. Try to use symbols. \n " ) ;
return 0 ;
}
/* Searching trace events corresponding to probe event */
2010-04-21 23:56:40 +04:00
ntevs = find_kprobe_trace_events ( fd , pev , tevs , max_tevs ) ;
2010-03-22 19:10:26 +03:00
close ( fd ) ;
2010-04-12 21:17:42 +04:00
if ( ntevs > 0 ) { /* Succeeded to find trace events */
pr_debug ( " find %d kprobe_trace_events. \n " , ntevs ) ;
2010-03-22 19:10:26 +03:00
return 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 ) {
pr_debug ( " Trying to use symbols. \n n " ) ;
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
}
# define LINEBUF_SIZE 256
# define NR_ADDITIONAL_LINES 2
2010-04-15 02:39:42 +04: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 ] ;
const char * color = PERF_COLOR_BLUE ;
if ( fgets ( buf , LINEBUF_SIZE , fp ) = = NULL )
goto error ;
if ( ! skip ) {
if ( show_num )
2010-04-15 02:39:42 +04:00
fprintf ( stdout , " %7d %s " , l , buf ) ;
2010-03-22 19:10:26 +03:00
else
color_fprintf ( stdout , color , " %s " , buf ) ;
}
while ( strlen ( buf ) = = LINEBUF_SIZE - 1 & &
buf [ LINEBUF_SIZE - 2 ] ! = ' \n ' ) {
if ( fgets ( buf , LINEBUF_SIZE , fp ) = = NULL )
goto error ;
if ( ! skip ) {
if ( show_num )
fprintf ( stdout , " %s " , buf ) ;
else
color_fprintf ( stdout , color , " %s " , buf ) ;
}
}
2010-04-12 21:17:42 +04:00
return 0 ;
2010-03-22 19:10:26 +03:00
error :
if ( feof ( fp ) )
2010-04-12 21:17:42 +04:00
pr_warning ( " Source file is shorter than expected. \n " ) ;
2010-03-22 19:10:26 +03:00
else
2010-04-12 21:17:42 +04:00
pr_warning ( " File read error: %s \n " , strerror ( errno ) ) ;
return - 1 ;
2010-03-22 19:10:26 +03:00
}
/*
* Show line - range always requires debuginfo to find source file and
* line number .
*/
2010-04-12 21:17:42 +04:00
int show_line_range ( struct line_range * lr )
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 ;
FILE * fp ;
int fd , ret ;
/* Search a line range */
2010-04-12 21:17:42 +04:00
ret = init_vmlinux ( ) ;
if ( ret < 0 )
return ret ;
2010-03-22 19:10:26 +03:00
fd = open_vmlinux ( ) ;
2010-04-12 21:17:42 +04:00
if ( fd < 0 ) {
pr_warning ( " Failed to open debuginfo file. \n " ) ;
return fd ;
}
2010-03-22 19:10:26 +03:00
ret = find_line_range ( fd , lr ) ;
close ( fd ) ;
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
setup_pager ( ) ;
if ( lr - > function )
fprintf ( stdout , " <%s:%d> \n " , lr - > function ,
lr - > start - lr - > offset ) ;
else
fprintf ( stdout , " <%s:%d> \n " , lr - > file , lr - > start ) ;
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-04-12 21:17:42 +04:00
while ( l < lr - > start & & ret > = 0 )
ret = show_one_line ( fp , l + + , true , false ) ;
if ( ret < 0 )
goto end ;
2010-03-22 19:10:26 +03:00
list_for_each_entry ( ln , & lr - > line_list , list ) {
2010-04-12 21:17:42 +04:00
while ( ln - > line > l & & ret > = 0 )
ret = show_one_line ( fp , ( l + + ) - lr - > offset ,
false , false ) ;
if ( ret > = 0 )
ret = show_one_line ( fp , ( l + + ) - lr - > offset ,
false , true ) ;
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-04-15 02:39:50 +04:00
while ( l < = lr - > end & & ! feof ( fp ) & & ret > = 0 )
2010-04-12 21:17:42 +04:00
ret = show_one_line ( fp , ( l + + ) - lr - > offset , false , false ) ;
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
}
# else /* !DWARF_SUPPORT */
2010-04-12 21:17:42 +04:00
static int convert_to_perf_probe_point ( struct kprobe_trace_point * tp ,
2010-03-22 19:10:26 +03:00
struct perf_probe_point * pp )
{
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
}
static int try_to_find_kprobe_trace_events ( struct perf_probe_event * pev ,
2010-04-21 23:56:40 +04:00
struct kprobe_trace_event * * tevs __unused ,
int max_tevs __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-04-12 21:17:42 +04:00
int show_line_range ( struct line_range * lr __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-03-17 01:05:37 +03:00
# endif
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
{
const char * ptr ;
char * tmp ;
/*
* < Syntax >
* SRC : SLN [ + NUM | - ELN ]
* FUNC [ : SLN [ + NUM | - ELN ] ]
*/
ptr = strchr ( arg , ' : ' ) ;
if ( ptr ) {
2010-04-15 02:39:42 +04:00
lr - > start = ( int ) strtoul ( ptr + 1 , & tmp , 0 ) ;
2010-04-15 02:39:50 +04:00
if ( * tmp = = ' + ' ) {
2010-04-15 02:39:42 +04:00
lr - > end = lr - > start + ( int ) strtoul ( tmp + 1 , & tmp , 0 ) ;
2010-04-15 02:39:50 +04:00
lr - > end - - ; /*
* 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 .
*/
} else if ( * tmp = = ' - ' )
2010-04-15 02:39:42 +04:00
lr - > end = ( int ) strtoul ( tmp + 1 , & tmp , 0 ) ;
2010-01-06 17:45:34 +03:00
else
2010-04-15 02:39:42 +04:00
lr - > end = INT_MAX ;
pr_debug ( " Line range is %d to %d \n " , lr - > start , lr - > end ) ;
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 " ) ;
return - EINVAL ;
}
if ( * tmp ! = ' \0 ' ) {
semantic_error ( " Tailing with invalid character '%d'. \n " ,
2010-01-06 17:45:34 +03:00
* tmp ) ;
2010-04-12 21:17:42 +04:00
return - EINVAL ;
}
2010-04-12 21:17:56 +04:00
tmp = strndup ( arg , ( ptr - arg ) ) ;
2010-04-15 02:39:42 +04:00
} else {
2010-04-12 21:17:56 +04:00
tmp = strdup ( arg ) ;
2010-04-15 02:39:42 +04:00
lr - > end = INT_MAX ;
}
2010-04-12 21:17:56 +04:00
if ( tmp = = NULL )
return - ENOMEM ;
2010-01-06 17:45:34 +03:00
if ( strchr ( tmp , ' . ' ) )
lr - > file = tmp ;
else
lr - > function = tmp ;
2010-04-12 21:17:42 +04:00
return 0 ;
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-02-25 16:36:12 +03:00
semantic_error ( " Lazy pattern can't be used with line number. " ) ;
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-02-25 16:36:12 +03:00
semantic_error ( " Lazy pattern can't be used with offset. " ) ;
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 ) {
2009-12-01 03:19:58 +03:00
semantic_error ( " Offset can't be used with line number. " ) ;
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 "
" lazy pattern. " ) ;
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 ) {
2009-12-01 03:19:58 +03:00
semantic_error ( " Offset requires an entry function. " ) ;
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 ) {
2009-12-01 03:19:58 +03:00
semantic_error ( " Return probe requires an entry function. " ) ;
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 "
" return probe. " ) ;
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-04-12 21:16:53 +04:00
char * tmp ;
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-03-17 01:06:26 +03:00
tmp = strpbrk ( str , " -. " ) ;
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
}
/* Structure fields */
2010-04-12 21:17:56 +04:00
arg - > var = strndup ( str , tmp - str ) ;
if ( arg - > var = = NULL )
return - ENOMEM ;
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-03-17 01:06:26 +03:00
if ( * tmp = = ' . ' ) {
str = tmp + 1 ;
( * fieldp ) - > ref = false ;
} else if ( tmp [ 1 ] = = ' > ' ) {
str = tmp + 2 ;
( * fieldp ) - > ref = true ;
2010-04-12 21:17:42 +04:00
} else {
semantic_error ( " Argument parse error: %s \n " , str ) ;
return - EINVAL ;
}
2010-03-17 01:06:26 +03:00
tmp = strpbrk ( str , " -. " ) ;
if ( tmp ) {
2010-04-12 21:17:56 +04:00
( * fieldp ) - > name = strndup ( str , tmp - str ) ;
if ( ( * fieldp ) - > name = = NULL )
return - ENOMEM ;
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-03-17 01:06:26 +03:00
pr_debug ( " %s(%d) \n " , ( * fieldp ) - > name , ( * fieldp ) - > ref ) ;
2010-04-12 21:17:00 +04:00
/* If no name is specified, set the last field name */
2010-04-12 21:17:56 +04:00
if ( ! arg - > name ) {
arg - > name = strdup ( ( * fieldp ) - > name ) ;
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 ;
}
2009-12-01 03:20:17 +03:00
/* Parse kprobe_events event into struct probe_point */
2010-04-12 21:17:42 +04:00
int parse_kprobe_trace_command ( const char * cmd , struct kprobe_trace_event * tev )
2009-12-01 03:20:17 +03:00
{
2010-03-17 01:06:12 +03:00
struct kprobe_trace_point * tp = & tev - > point ;
2009-12-01 03:20:17 +03:00
char pr ;
char * p ;
int ret , i , argc ;
char * * argv ;
2010-03-17 01:06:12 +03:00
pr_debug ( " Parsing kprobe_events: %s \n " , cmd ) ;
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
/* Scan function name and offset */
2010-03-17 01:06:12 +03:00
ret = sscanf ( argv [ 1 ] , " %a[^+]+%lu " , ( float * ) ( void * ) & tp - > symbol ,
& 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-04-12 21:17:49 +04:00
tev - > args = zalloc ( sizeof ( struct kprobe_trace_arg ) * tev - > nargs ) ;
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 ) {
ret = e_snprintf ( tmp , len , " %s%s " , field - > ref ? " -> " : " . " ,
field - > name ) ;
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-04-12 21:17:42 +04:00
pr_debug ( " Failed to synthesize perf probe argument: %s " ,
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-04-15 02:39:35 +04:00
len = strlen ( pp - > file ) - 31 ;
2010-03-17 01:06:19 +03:00
if ( len < 0 )
len = 0 ;
tmp = strchr ( pp - > file + len , ' / ' ) ;
if ( ! tmp )
2010-04-15 02:39:35 +04:00
tmp = pp - > file + len ;
2010-03-17 01:06:19 +03:00
ret = e_snprintf ( file , 32 , " @%s " , tmp + 1 ) ;
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-04-12 21:17:42 +04:00
pr_debug ( " Failed to synthesize perf probe point: %s " ,
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
static int __synthesize_kprobe_trace_arg_ref ( struct kprobe_trace_arg_ref * ref ,
char * * buf , size_t * buflen ,
int depth )
{
int ret ;
if ( ref - > next ) {
depth = __synthesize_kprobe_trace_arg_ref ( ref - > next , buf ,
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-03-17 01:06:12 +03:00
static int synthesize_kprobe_trace_arg ( struct kprobe_trace_arg * arg ,
char * buf , size_t buflen )
2009-12-01 03:19:58 +03:00
{
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 ;
/* Dereferencing arguments */
if ( arg - > ref ) {
depth = __synthesize_kprobe_trace_arg_ref ( arg - > ref , & buf ,
& buflen , 1 ) ;
if ( depth < 0 )
return depth ;
}
/* Print argument value */
ret = e_snprintf ( buf , buflen , " %s " , arg - > value ) ;
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 ;
}
char * synthesize_kprobe_trace_command ( struct kprobe_trace_event * tev )
{
struct kprobe_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 ;
2010-03-17 01:06:12 +03:00
len = e_snprintf ( buf , MAX_CMDLEN , " %c:%s/%s %s+%lu " ,
tp - > retprobe ? ' r ' : ' p ' ,
tev - > group , tev - > event ,
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 + + ) {
ret = synthesize_kprobe_trace_arg ( & tev - > args [ i ] , buf + len ,
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-04-12 21:17:42 +04:00
int convert_to_perf_probe_event ( struct kprobe_trace_event * tev ,
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-04-12 21:17:42 +04:00
ret = convert_to_perf_probe_point ( & tev - > point , & pev - > point ) ;
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-04-12 21:17:42 +04:00
ret = synthesize_kprobe_trace_arg ( & tev - > args [ i ] ,
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 ) ) ;
}
void clear_kprobe_trace_event ( struct kprobe_trace_event * tev )
{
struct kprobe_trace_arg_ref * ref , * next ;
int i ;
if ( tev - > event )
free ( tev - > event ) ;
if ( tev - > group )
free ( tev - > group ) ;
if ( tev - > point . symbol )
free ( tev - > point . symbol ) ;
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-03-17 01:06:12 +03:00
static struct strlist * get_kprobe_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-03-17 01:06:12 +03:00
struct kprobe_trace_event tev ;
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-03-17 01:06:12 +03:00
rawlist = get_kprobe_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-04-12 21:17:42 +04:00
ret = parse_kprobe_trace_command ( ent - > s , & tev ) ;
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 ) ;
clear_kprobe_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-03-17 01:06:12 +03:00
static struct strlist * get_kprobe_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-03-17 01:06:12 +03:00
struct kprobe_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 ) ) ;
2009-12-01 03:20:25 +03:00
2010-03-17 01:06:12 +03:00
rawlist = get_kprobe_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-04-12 21:17:42 +04:00
ret = parse_kprobe_trace_command ( ent - > s , & tev ) ;
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-03-17 01:06:12 +03:00
clear_kprobe_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-04-12 21:17:42 +04:00
static int write_kprobe_trace_event ( int fd , struct kprobe_trace_event * tev )
2009-12-01 03:19:58 +03:00
{
2010-04-21 04:01:05 +04:00
int ret = 0 ;
2010-03-17 01:06:12 +03:00
char * buf = synthesize_kprobe_trace_command ( tev ) ;
2009-12-01 03:19:58 +03:00
2010-04-12 21:17:42 +04:00
if ( ! buf ) {
pr_debug ( " Failed to synthesize kprobe trace event. \n " ) ;
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-04-12 21:17:42 +04:00
static int __add_kprobe_trace_events ( struct perf_probe_event * pev ,
struct kprobe_trace_event * tevs ,
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-03-18 18:51:16 +03:00
struct kprobe_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-03-17 01:06:12 +03:00
namelist = get_kprobe_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-04-12 21:17:42 +04:00
ret = write_kprobe_trace_event ( fd , tev ) ;
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-03-17 01:06:12 +03:00
static int convert_to_kprobe_trace_events ( struct perf_probe_event * pev ,
2010-04-21 23:56:40 +04:00
struct kprobe_trace_event * * tevs ,
int max_tevs )
2010-03-17 01:05:37 +03:00
{
struct symbol * sym ;
2010-04-12 21:17:49 +04:00
int ret = 0 , i ;
2010-03-17 01:06:12 +03:00
struct kprobe_trace_event * tev ;
2010-03-22 19:10:26 +03:00
/* Convert perf_probe_event with debuginfo */
2010-04-21 23:56:40 +04:00
ret = try_to_find_kprobe_trace_events ( pev , tevs , max_tevs ) ;
2010-04-12 21:17:49 +04:00
if ( ret ! = 0 )
return ret ;
2010-03-17 01:05:37 +03:00
2010-03-17 01:06:12 +03:00
/* Allocate trace event buffer */
2010-04-12 21:17:49 +04:00
tev = * tevs = zalloc ( sizeof ( struct kprobe_trace_event ) ) ;
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 ;
}
2010-03-17 01:06:12 +03:00
tev - > point . offset = pev - > point . offset ;
tev - > nargs = pev - > nargs ;
if ( tev - > nargs ) {
2010-04-12 21:17:49 +04:00
tev - > args = zalloc ( sizeof ( struct kprobe_trace_arg )
* 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-04-28 04:20:43 +04:00
sym = map__find_symbol_by_name ( machine . vmlinux_maps [ MAP__FUNCTION ] ,
2010-03-17 01:06:12 +03:00
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 :
clear_kprobe_trace_event ( tev ) ;
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 ;
struct kprobe_trace_event * tevs ;
int ntevs ;
} ;
2010-04-12 21:17:42 +04:00
int add_perf_probe_events ( struct perf_probe_event * pevs , int npevs ,
2010-04-21 23:56:40 +04:00
bool force_add , int max_tevs )
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 ( ) ;
if ( ret < 0 )
return ret ;
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-04-12 21:17:42 +04:00
ret = convert_to_kprobe_trace_events ( pkgs [ i ] . pev ,
2010-04-21 23:56:40 +04:00
& pkgs [ i ] . tevs , max_tevs ) ;
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 */
2010-04-12 21:17:42 +04:00
for ( i = 0 ; i < npevs & & ret > = 0 ; i + + )
ret = __add_kprobe_trace_events ( pkgs [ i ] . pev , pkgs [ i ] . tevs ,
pkgs [ i ] . ntevs , force_add ) ;
end :
/* Loop 3: cleanup trace events */
2010-03-17 01:06:12 +03:00
for ( i = 0 ; i < npevs ; i + + )
2010-04-12 21:17:42 +04:00
for ( j = 0 ; j < pkgs [ i ] . ntevs ; j + + )
clear_kprobe_trace_event ( & pkgs [ i ] . tevs [ j ] ) ;
return ret ;
2010-03-17 01:05:37 +03:00
}
2010-04-12 21:17:42 +04:00
static int __del_trace_kprobe_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
/* Convert from perf-probe event to trace-kprobe 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 ) ) ;
2010-04-12 21:17:42 +04:00
if ( ret < 0 )
goto error ;
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-04-12 21:17:42 +04:00
static int del_trace_kprobe_event ( int fd , const char * group ,
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 ) {
pr_err ( " Failed to copy event. " ) ;
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-04-12 21:17:42 +04:00
ret = __del_trace_kprobe_event ( fd , ent ) ;
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-04-12 21:17:42 +04:00
ret = __del_trace_kprobe_event ( fd , ent ) ;
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-03-17 01:06:12 +03:00
namelist = get_kprobe_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-04-12 21:17:42 +04:00
ret = del_trace_kprobe_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
}