2012-04-09 13:11:44 +04:00
/*
* Common code for probe - based Dynamic events .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* 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
*
* This code was copied from kernel / trace / trace_kprobe . c written by
* Masami Hiramatsu < masami . hiramatsu . pt @ hitachi . com >
*
* Updates to make this generic :
* Copyright ( C ) IBM Corporation , 2010 - 2011
* Author : Srikar Dronamraju
*/
# include "trace_probe.h"
const char * reserved_field_names [ ] = {
" common_type " ,
" common_flags " ,
" common_preempt_count " ,
" common_pid " ,
" common_tgid " ,
FIELD_STRING_IP ,
FIELD_STRING_RETIP ,
FIELD_STRING_FUNC ,
} ;
/* Printing in basic type function template */
2016-08-18 11:57:50 +03:00
# define DEFINE_BASIC_PRINT_TYPE_FUNC(tname, type, fmt) \
int PRINT_TYPE_FUNC_NAME ( tname ) ( struct trace_seq * s , const char * name , \
2014-04-17 12:18:28 +04:00
void * data , void * ent ) \
2012-04-09 13:11:44 +04:00
{ \
2014-11-13 01:19:51 +03:00
trace_seq_printf ( s , " %s= " fmt , name , * ( type * ) data ) ; \
return ! trace_seq_has_overflowed ( s ) ; \
2012-04-09 13:11:44 +04:00
} \
2016-08-18 11:57:50 +03:00
const char PRINT_TYPE_FMT_NAME ( tname ) [ ] = fmt ; \
NOKPROBE_SYMBOL ( PRINT_TYPE_FUNC_NAME ( tname ) ) ;
2012-04-09 13:11:44 +04:00
2016-08-18 11:57:50 +03:00
DEFINE_BASIC_PRINT_TYPE_FUNC ( u8 , u8 , " 0x%x " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( u16 , u16 , " 0x%x " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( u32 , u32 , " 0x%x " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( u64 , u64 , " 0x%Lx " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( s8 , s8 , " %d " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( s16 , s16 , " %d " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( s32 , s32 , " %d " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( s64 , s64 , " %Ld " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( x8 , u8 , " 0x%x " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( x16 , u16 , " 0x%x " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( x32 , u32 , " 0x%x " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( x64 , u64 , " 0x%Lx " )
2012-04-09 13:11:44 +04:00
/* Print type function for string type */
2014-04-17 12:18:28 +04:00
int PRINT_TYPE_FUNC_NAME ( string ) ( struct trace_seq * s , const char * name ,
void * data , void * ent )
2012-04-09 13:11:44 +04:00
{
int len = * ( u32 * ) data > > 16 ;
if ( ! len )
2014-11-13 01:19:51 +03:00
trace_seq_printf ( s , " %s=(fault) " , name ) ;
2012-04-09 13:11:44 +04:00
else
2014-11-13 01:19:51 +03:00
trace_seq_printf ( s , " %s= \" %s \" " , name ,
( const char * ) get_loc_data ( data , ent ) ) ;
return ! trace_seq_has_overflowed ( s ) ;
2012-04-09 13:11:44 +04:00
}
2014-04-17 12:18:28 +04:00
NOKPROBE_SYMBOL ( PRINT_TYPE_FUNC_NAME ( string ) ) ;
2012-04-09 13:11:44 +04:00
2013-11-26 09:19:59 +04:00
const char PRINT_TYPE_FMT_NAME ( string ) [ ] = " \\ \" %s \\ \" " ;
2012-04-09 13:11:44 +04:00
# define CHECK_FETCH_FUNCS(method, fn) \
( ( ( FETCH_FUNC_NAME ( method , u8 ) = = fn ) | | \
( FETCH_FUNC_NAME ( method , u16 ) = = fn ) | | \
( FETCH_FUNC_NAME ( method , u32 ) = = fn ) | | \
( FETCH_FUNC_NAME ( method , u64 ) = = fn ) | | \
( FETCH_FUNC_NAME ( method , string ) = = fn ) | | \
( FETCH_FUNC_NAME ( method , string_size ) = = fn ) ) \
& & ( fn ! = NULL ) )
/* Data fetch function templates */
# define DEFINE_FETCH_reg(type) \
2014-04-17 12:18:28 +04:00
void FETCH_FUNC_NAME ( reg , type ) ( struct pt_regs * regs , void * offset , void * dest ) \
2012-04-09 13:11:44 +04:00
{ \
* ( type * ) dest = ( type ) regs_get_register ( regs , \
( unsigned int ) ( ( unsigned long ) offset ) ) ; \
2014-04-17 12:18:28 +04:00
} \
NOKPROBE_SYMBOL ( FETCH_FUNC_NAME ( reg , type ) ) ;
2012-04-09 13:11:44 +04:00
DEFINE_BASIC_FETCH_FUNCS ( reg )
/* No string on the register */
# define fetch_reg_string NULL
# define fetch_reg_string_size NULL
# define DEFINE_FETCH_retval(type) \
2014-04-17 12:18:28 +04:00
void FETCH_FUNC_NAME ( retval , type ) ( struct pt_regs * regs , \
void * dummy , void * dest ) \
2012-04-09 13:11:44 +04:00
{ \
* ( type * ) dest = ( type ) regs_return_value ( regs ) ; \
2014-04-17 12:18:28 +04:00
} \
NOKPROBE_SYMBOL ( FETCH_FUNC_NAME ( retval , type ) ) ;
2012-04-09 13:11:44 +04:00
DEFINE_BASIC_FETCH_FUNCS ( retval )
/* No string on the retval */
# define fetch_retval_string NULL
# define fetch_retval_string_size NULL
/* Dereference memory access function */
struct deref_fetch_param {
struct fetch_param orig ;
long offset ;
2013-07-01 08:44:32 +04:00
fetch_func_t fetch ;
fetch_func_t fetch_size ;
2012-04-09 13:11:44 +04:00
} ;
# define DEFINE_FETCH_deref(type) \
2014-04-17 12:18:28 +04:00
void FETCH_FUNC_NAME ( deref , type ) ( struct pt_regs * regs , \
void * data , void * dest ) \
2012-04-09 13:11:44 +04:00
{ \
struct deref_fetch_param * dprm = data ; \
unsigned long addr ; \
call_fetch ( & dprm - > orig , regs , & addr ) ; \
if ( addr ) { \
addr + = dprm - > offset ; \
2013-07-01 08:44:32 +04:00
dprm - > fetch ( regs , ( void * ) addr , dest ) ; \
2012-04-09 13:11:44 +04:00
} else \
* ( type * ) dest = 0 ; \
2014-04-17 12:18:28 +04:00
} \
NOKPROBE_SYMBOL ( FETCH_FUNC_NAME ( deref , type ) ) ;
2012-04-09 13:11:44 +04:00
DEFINE_BASIC_FETCH_FUNCS ( deref )
DEFINE_FETCH_deref ( string )
2013-07-01 08:44:32 +04:00
2014-04-17 12:18:28 +04:00
void FETCH_FUNC_NAME ( deref , string_size ) ( struct pt_regs * regs ,
void * data , void * dest )
2013-07-01 08:44:32 +04:00
{
struct deref_fetch_param * dprm = data ;
unsigned long addr ;
call_fetch ( & dprm - > orig , regs , & addr ) ;
if ( addr & & dprm - > fetch_size ) {
addr + = dprm - > offset ;
dprm - > fetch_size ( regs , ( void * ) addr , dest ) ;
} else
* ( string_size * ) dest = 0 ;
}
2014-04-17 12:18:28 +04:00
NOKPROBE_SYMBOL ( FETCH_FUNC_NAME ( deref , string_size ) ) ;
2012-04-09 13:11:44 +04:00
2014-04-17 12:18:28 +04:00
static void update_deref_fetch_param ( struct deref_fetch_param * data )
2012-04-09 13:11:44 +04:00
{
if ( CHECK_FETCH_FUNCS ( deref , data - > orig . fn ) )
update_deref_fetch_param ( data - > orig . data ) ;
else if ( CHECK_FETCH_FUNCS ( symbol , data - > orig . fn ) )
update_symbol_cache ( data - > orig . data ) ;
}
2014-04-17 12:18:28 +04:00
NOKPROBE_SYMBOL ( update_deref_fetch_param ) ;
2012-04-09 13:11:44 +04:00
2014-04-17 12:18:28 +04:00
static void free_deref_fetch_param ( struct deref_fetch_param * data )
2012-04-09 13:11:44 +04:00
{
if ( CHECK_FETCH_FUNCS ( deref , data - > orig . fn ) )
free_deref_fetch_param ( data - > orig . data ) ;
else if ( CHECK_FETCH_FUNCS ( symbol , data - > orig . fn ) )
free_symbol_cache ( data - > orig . data ) ;
kfree ( data ) ;
}
2014-04-17 12:18:28 +04:00
NOKPROBE_SYMBOL ( free_deref_fetch_param ) ;
2012-04-09 13:11:44 +04:00
/* Bitfield fetch function */
struct bitfield_fetch_param {
struct fetch_param orig ;
unsigned char hi_shift ;
unsigned char low_shift ;
} ;
# define DEFINE_FETCH_bitfield(type) \
2014-04-17 12:18:28 +04:00
void FETCH_FUNC_NAME ( bitfield , type ) ( struct pt_regs * regs , \
void * data , void * dest ) \
2012-04-09 13:11:44 +04:00
{ \
struct bitfield_fetch_param * bprm = data ; \
type buf = 0 ; \
call_fetch ( & bprm - > orig , regs , & buf ) ; \
if ( buf ) { \
buf < < = bprm - > hi_shift ; \
buf > > = bprm - > low_shift ; \
} \
* ( type * ) dest = buf ; \
2014-04-17 12:18:28 +04:00
} \
NOKPROBE_SYMBOL ( FETCH_FUNC_NAME ( bitfield , type ) ) ;
2012-04-09 13:11:44 +04:00
DEFINE_BASIC_FETCH_FUNCS ( bitfield )
# define fetch_bitfield_string NULL
# define fetch_bitfield_string_size NULL
2014-04-17 12:18:00 +04:00
static void
2012-04-09 13:11:44 +04:00
update_bitfield_fetch_param ( struct bitfield_fetch_param * data )
{
/*
* Don ' t check the bitfield itself , because this must be the
* last fetch function .
*/
if ( CHECK_FETCH_FUNCS ( deref , data - > orig . fn ) )
update_deref_fetch_param ( data - > orig . data ) ;
else if ( CHECK_FETCH_FUNCS ( symbol , data - > orig . fn ) )
update_symbol_cache ( data - > orig . data ) ;
}
2014-04-17 12:18:00 +04:00
static void
2012-04-09 13:11:44 +04:00
free_bitfield_fetch_param ( struct bitfield_fetch_param * data )
{
/*
* Don ' t check the bitfield itself , because this must be the
* last fetch function .
*/
if ( CHECK_FETCH_FUNCS ( deref , data - > orig . fn ) )
free_deref_fetch_param ( data - > orig . data ) ;
else if ( CHECK_FETCH_FUNCS ( symbol , data - > orig . fn ) )
free_symbol_cache ( data - > orig . data ) ;
kfree ( data ) ;
}
2016-06-09 04:38:02 +03:00
void FETCH_FUNC_NAME ( comm , string ) ( struct pt_regs * regs ,
void * data , void * dest )
{
int maxlen = get_rloc_len ( * ( u32 * ) dest ) ;
u8 * dst = get_rloc_data ( dest ) ;
long ret ;
if ( ! maxlen )
return ;
ret = strlcpy ( dst , current - > comm , maxlen ) ;
* ( u32 * ) dest = make_data_rloc ( ret , get_rloc_offs ( * ( u32 * ) dest ) ) ;
}
NOKPROBE_SYMBOL ( FETCH_FUNC_NAME ( comm , string ) ) ;
void FETCH_FUNC_NAME ( comm , string_size ) ( struct pt_regs * regs ,
void * data , void * dest )
{
* ( u32 * ) dest = strlen ( current - > comm ) + 1 ;
}
NOKPROBE_SYMBOL ( FETCH_FUNC_NAME ( comm , string_size ) ) ;
2013-11-26 09:56:28 +04:00
static const struct fetch_type * find_fetch_type ( const char * type ,
const struct fetch_type * ftbl )
2012-04-09 13:11:44 +04:00
{
int i ;
if ( ! type )
type = DEFAULT_FETCH_TYPE_STR ;
/* Special case: bitfield */
if ( * type = = ' b ' ) {
unsigned long bs ;
type = strchr ( type , ' / ' ) ;
if ( ! type )
goto fail ;
type + + ;
2012-09-27 00:08:38 +04:00
if ( kstrtoul ( type , 0 , & bs ) )
2012-04-09 13:11:44 +04:00
goto fail ;
switch ( bs ) {
case 8 :
2013-11-26 09:56:28 +04:00
return find_fetch_type ( " u8 " , ftbl ) ;
2012-04-09 13:11:44 +04:00
case 16 :
2013-11-26 09:56:28 +04:00
return find_fetch_type ( " u16 " , ftbl ) ;
2012-04-09 13:11:44 +04:00
case 32 :
2013-11-26 09:56:28 +04:00
return find_fetch_type ( " u32 " , ftbl ) ;
2012-04-09 13:11:44 +04:00
case 64 :
2013-11-26 09:56:28 +04:00
return find_fetch_type ( " u64 " , ftbl ) ;
2012-04-09 13:11:44 +04:00
default :
goto fail ;
}
}
2013-11-26 09:56:28 +04:00
for ( i = 0 ; ftbl [ i ] . name ; i + + ) {
if ( strcmp ( type , ftbl [ i ] . name ) = = 0 )
return & ftbl [ i ] ;
}
2012-04-09 13:11:44 +04:00
fail :
return NULL ;
}
/* Special function : only accept unsigned long */
2014-04-17 12:18:28 +04:00
static void fetch_kernel_stack_address ( struct pt_regs * regs , void * dummy , void * dest )
2012-04-09 13:11:44 +04:00
{
* ( unsigned long * ) dest = kernel_stack_pointer ( regs ) ;
}
2014-04-17 12:18:28 +04:00
NOKPROBE_SYMBOL ( fetch_kernel_stack_address ) ;
2012-04-09 13:11:44 +04:00
2014-04-17 12:18:28 +04:00
static void fetch_user_stack_address ( struct pt_regs * regs , void * dummy , void * dest )
2013-07-03 13:34:23 +04:00
{
* ( unsigned long * ) dest = user_stack_pointer ( regs ) ;
}
2014-04-17 12:18:28 +04:00
NOKPROBE_SYMBOL ( fetch_user_stack_address ) ;
2013-07-03 13:34:23 +04:00
2012-04-09 13:11:44 +04:00
static fetch_func_t get_fetch_size_function ( const struct fetch_type * type ,
2013-11-26 09:56:28 +04:00
fetch_func_t orig_fn ,
const struct fetch_type * ftbl )
2012-04-09 13:11:44 +04:00
{
int i ;
2013-11-26 09:56:28 +04:00
if ( type ! = & ftbl [ FETCH_TYPE_STRING ] )
2012-04-09 13:11:44 +04:00
return NULL ; /* Only string type needs size function */
for ( i = 0 ; i < FETCH_MTD_END ; i + + )
if ( type - > fetch [ i ] = = orig_fn )
2013-11-26 09:56:28 +04:00
return ftbl [ FETCH_TYPE_STRSIZE ] . fetch [ i ] ;
2012-04-09 13:11:44 +04:00
WARN_ON ( 1 ) ; /* This should not happen */
return NULL ;
}
/* Split symbol and offset. */
int traceprobe_split_symbol_offset ( char * symbol , unsigned long * offset )
{
char * tmp ;
int ret ;
if ( ! offset )
return - EINVAL ;
tmp = strchr ( symbol , ' + ' ) ;
if ( tmp ) {
2012-09-27 00:08:38 +04:00
/* skip sign because kstrtoul doesn't accept '+' */
ret = kstrtoul ( tmp + 1 , 0 , offset ) ;
2012-04-09 13:11:44 +04:00
if ( ret )
return ret ;
* tmp = ' \0 ' ;
} else
* offset = 0 ;
return 0 ;
}
# define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
static int parse_probe_vars ( char * arg , const struct fetch_type * t ,
2013-07-03 13:34:23 +04:00
struct fetch_param * f , bool is_return ,
bool is_kprobe )
2012-04-09 13:11:44 +04:00
{
int ret = 0 ;
unsigned long param ;
if ( strcmp ( arg , " retval " ) = = 0 ) {
if ( is_return )
f - > fn = t - > fetch [ FETCH_MTD_retval ] ;
else
ret = - EINVAL ;
} else if ( strncmp ( arg , " stack " , 5 ) = = 0 ) {
if ( arg [ 5 ] = = ' \0 ' ) {
2013-07-03 13:34:23 +04:00
if ( strcmp ( t - > name , DEFAULT_FETCH_TYPE_STR ) )
return - EINVAL ;
if ( is_kprobe )
f - > fn = fetch_kernel_stack_address ;
2012-04-09 13:11:44 +04:00
else
2013-07-03 13:34:23 +04:00
f - > fn = fetch_user_stack_address ;
2012-04-09 13:11:44 +04:00
} else if ( isdigit ( arg [ 5 ] ) ) {
2012-09-27 00:08:38 +04:00
ret = kstrtoul ( arg + 5 , 10 , & param ) ;
2013-07-03 13:34:23 +04:00
if ( ret | | ( is_kprobe & & param > PARAM_MAX_STACK ) )
2012-04-09 13:11:44 +04:00
ret = - EINVAL ;
else {
f - > fn = t - > fetch [ FETCH_MTD_stack ] ;
f - > data = ( void * ) param ;
}
} else
ret = - EINVAL ;
2016-06-09 04:38:02 +03:00
} else if ( strcmp ( arg , " comm " ) = = 0 ) {
if ( strcmp ( t - > name , " string " ) ! = 0 & &
strcmp ( t - > name , " string_size " ) ! = 0 )
return - EINVAL ;
f - > fn = t - > fetch [ FETCH_MTD_comm ] ;
2012-04-09 13:11:44 +04:00
} else
ret = - EINVAL ;
return ret ;
}
/* Recursive argument parser */
static int parse_probe_arg ( char * arg , const struct fetch_type * t ,
2015-03-12 08:58:34 +03:00
struct fetch_param * f , bool is_return , bool is_kprobe ,
const struct fetch_type * ftbl )
2012-04-09 13:11:44 +04:00
{
unsigned long param ;
long offset ;
char * tmp ;
2013-11-26 09:56:28 +04:00
int ret = 0 ;
2012-04-09 13:11:44 +04:00
switch ( arg [ 0 ] ) {
case ' $ ' :
2013-07-03 13:34:23 +04:00
ret = parse_probe_vars ( arg + 1 , t , f , is_return , is_kprobe ) ;
2012-04-09 13:11:44 +04:00
break ;
case ' % ' : /* named register */
ret = regs_query_register_offset ( arg + 1 ) ;
if ( ret > = 0 ) {
f - > fn = t - > fetch [ FETCH_MTD_reg ] ;
f - > data = ( void * ) ( unsigned long ) ret ;
ret = 0 ;
}
break ;
2013-11-25 08:42:47 +04:00
case ' @ ' : /* memory, file-offset or symbol */
2012-04-09 13:11:44 +04:00
if ( isdigit ( arg [ 1 ] ) ) {
2012-09-27 00:08:38 +04:00
ret = kstrtoul ( arg + 1 , 0 , & param ) ;
2012-04-09 13:11:44 +04:00
if ( ret )
break ;
f - > fn = t - > fetch [ FETCH_MTD_memory ] ;
f - > data = ( void * ) param ;
2013-11-25 08:42:47 +04:00
} else if ( arg [ 1 ] = = ' + ' ) {
/* kprobes don't support file offsets */
if ( is_kprobe )
return - EINVAL ;
ret = kstrtol ( arg + 2 , 0 , & offset ) ;
if ( ret )
break ;
f - > fn = t - > fetch [ FETCH_MTD_file_offset ] ;
f - > data = ( void * ) offset ;
2012-04-09 13:11:44 +04:00
} else {
2013-07-03 13:34:23 +04:00
/* uprobes don't support symbols */
if ( ! is_kprobe )
return - EINVAL ;
2012-04-09 13:11:44 +04:00
ret = traceprobe_split_symbol_offset ( arg + 1 , & offset ) ;
if ( ret )
break ;
f - > data = alloc_symbol_cache ( arg + 1 , offset ) ;
if ( f - > data )
f - > fn = t - > fetch [ FETCH_MTD_symbol ] ;
}
break ;
case ' + ' : /* deref memory */
2012-09-27 00:08:38 +04:00
arg + + ; /* Skip '+', because kstrtol() rejects it. */
2012-04-09 13:11:44 +04:00
case ' - ' :
tmp = strchr ( arg , ' ( ' ) ;
if ( ! tmp )
break ;
* tmp = ' \0 ' ;
2012-09-27 00:08:38 +04:00
ret = kstrtol ( arg , 0 , & offset ) ;
2012-04-09 13:11:44 +04:00
if ( ret )
break ;
arg = tmp + 1 ;
tmp = strrchr ( arg , ' ) ' ) ;
if ( tmp ) {
struct deref_fetch_param * dprm ;
const struct fetch_type * t2 ;
2013-11-26 09:56:28 +04:00
t2 = find_fetch_type ( NULL , ftbl ) ;
2012-04-09 13:11:44 +04:00
* tmp = ' \0 ' ;
dprm = kzalloc ( sizeof ( struct deref_fetch_param ) , GFP_KERNEL ) ;
if ( ! dprm )
return - ENOMEM ;
dprm - > offset = offset ;
2013-07-01 08:44:32 +04:00
dprm - > fetch = t - > fetch [ FETCH_MTD_memory ] ;
dprm - > fetch_size = get_fetch_size_function ( t ,
dprm - > fetch , ftbl ) ;
2012-04-11 14:30:43 +04:00
ret = parse_probe_arg ( arg , t2 , & dprm - > orig , is_return ,
2015-03-12 08:58:34 +03:00
is_kprobe , ftbl ) ;
2012-04-09 13:11:44 +04:00
if ( ret )
kfree ( dprm ) ;
else {
f - > fn = t - > fetch [ FETCH_MTD_deref ] ;
f - > data = ( void * ) dprm ;
}
}
break ;
}
if ( ! ret & & ! f - > fn ) { /* Parsed, but do not find fetch method */
pr_info ( " %s type has no corresponding fetch method. \n " , t - > name ) ;
ret = - EINVAL ;
}
return ret ;
}
# define BYTES_TO_BITS(nb) ((BITS_PER_LONG * (nb)) / sizeof(long))
/* Bitfield type needs to be parsed into a fetch function */
static int __parse_bitfield_probe_arg ( const char * bf ,
const struct fetch_type * t ,
struct fetch_param * f )
{
struct bitfield_fetch_param * bprm ;
unsigned long bw , bo ;
char * tail ;
if ( * bf ! = ' b ' )
return 0 ;
bprm = kzalloc ( sizeof ( * bprm ) , GFP_KERNEL ) ;
if ( ! bprm )
return - ENOMEM ;
bprm - > orig = * f ;
f - > fn = t - > fetch [ FETCH_MTD_bitfield ] ;
f - > data = ( void * ) bprm ;
bw = simple_strtoul ( bf + 1 , & tail , 0 ) ; /* Use simple one */
if ( bw = = 0 | | * tail ! = ' @ ' )
return - EINVAL ;
bf = tail + 1 ;
bo = simple_strtoul ( bf , & tail , 0 ) ;
if ( tail = = bf | | * tail ! = ' / ' )
return - EINVAL ;
bprm - > hi_shift = BYTES_TO_BITS ( t - > size ) - ( bw + bo ) ;
bprm - > low_shift = bprm - > hi_shift + bo ;
return ( BYTES_TO_BITS ( t - > size ) < ( bw + bo ) ) ? - EINVAL : 0 ;
}
/* String length checking wrapper */
int traceprobe_parse_probe_arg ( char * arg , ssize_t * size ,
2015-03-12 08:58:34 +03:00
struct probe_arg * parg , bool is_return , bool is_kprobe ,
const struct fetch_type * ftbl )
2012-04-09 13:11:44 +04:00
{
const char * t ;
int ret ;
if ( strlen ( arg ) > MAX_ARGSTR_LEN ) {
pr_info ( " Argument is too long.: %s \n " , arg ) ;
return - ENOSPC ;
}
parg - > comm = kstrdup ( arg , GFP_KERNEL ) ;
if ( ! parg - > comm ) {
pr_info ( " Failed to allocate memory for command '%s'. \n " , arg ) ;
return - ENOMEM ;
}
t = strchr ( parg - > comm , ' : ' ) ;
if ( t ) {
arg [ t - parg - > comm ] = ' \0 ' ;
t + + ;
}
2016-06-09 04:38:02 +03:00
/*
* The default type of $ comm should be " string " , and it can ' t be
* dereferenced .
*/
if ( ! t & & strcmp ( arg , " $comm " ) = = 0 )
t = " string " ;
2013-11-26 09:56:28 +04:00
parg - > type = find_fetch_type ( t , ftbl ) ;
2012-04-09 13:11:44 +04:00
if ( ! parg - > type ) {
pr_info ( " Unsupported type: %s \n " , t ) ;
return - EINVAL ;
}
parg - > offset = * size ;
* size + = parg - > type - > size ;
2015-03-12 08:58:34 +03:00
ret = parse_probe_arg ( arg , parg - > type , & parg - > fetch , is_return ,
is_kprobe , ftbl ) ;
2012-04-09 13:11:44 +04:00
if ( ret > = 0 & & t ! = NULL )
ret = __parse_bitfield_probe_arg ( t , parg - > type , & parg - > fetch ) ;
if ( ret > = 0 ) {
parg - > fetch_size . fn = get_fetch_size_function ( parg - > type ,
2013-11-26 09:56:28 +04:00
parg - > fetch . fn ,
ftbl ) ;
2012-04-09 13:11:44 +04:00
parg - > fetch_size . data = parg - > fetch . data ;
}
return ret ;
}
/* Return 1 if name is reserved or already used by another argument */
int traceprobe_conflict_field_name ( const char * name ,
struct probe_arg * args , int narg )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( reserved_field_names ) ; i + + )
if ( strcmp ( reserved_field_names [ i ] , name ) = = 0 )
return 1 ;
for ( i = 0 ; i < narg ; i + + )
if ( strcmp ( args [ i ] . name , name ) = = 0 )
return 1 ;
return 0 ;
}
void traceprobe_update_arg ( struct probe_arg * arg )
{
if ( CHECK_FETCH_FUNCS ( bitfield , arg - > fetch . fn ) )
update_bitfield_fetch_param ( arg - > fetch . data ) ;
else if ( CHECK_FETCH_FUNCS ( deref , arg - > fetch . fn ) )
update_deref_fetch_param ( arg - > fetch . data ) ;
else if ( CHECK_FETCH_FUNCS ( symbol , arg - > fetch . fn ) )
update_symbol_cache ( arg - > fetch . data ) ;
}
void traceprobe_free_probe_arg ( struct probe_arg * arg )
{
if ( CHECK_FETCH_FUNCS ( bitfield , arg - > fetch . fn ) )
free_bitfield_fetch_param ( arg - > fetch . data ) ;
else if ( CHECK_FETCH_FUNCS ( deref , arg - > fetch . fn ) )
free_deref_fetch_param ( arg - > fetch . data ) ;
else if ( CHECK_FETCH_FUNCS ( symbol , arg - > fetch . fn ) )
free_symbol_cache ( arg - > fetch . data ) ;
kfree ( arg - > name ) ;
kfree ( arg - > comm ) ;
}
int traceprobe_command ( const char * buf , int ( * createfn ) ( int , char * * ) )
{
char * * argv ;
int argc , ret ;
argc = 0 ;
ret = 0 ;
argv = argv_split ( GFP_KERNEL , buf , & argc ) ;
if ( ! argv )
return - ENOMEM ;
if ( argc )
ret = createfn ( argc , argv ) ;
argv_free ( argv ) ;
return ret ;
}
# define WRITE_BUFSIZE 4096
ssize_t traceprobe_probes_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * ppos ,
int ( * createfn ) ( int , char * * ) )
{
char * kbuf , * tmp ;
int ret = 0 ;
size_t done = 0 ;
size_t size ;
kbuf = kmalloc ( WRITE_BUFSIZE , GFP_KERNEL ) ;
if ( ! kbuf )
return - ENOMEM ;
while ( done < count ) {
size = count - done ;
if ( size > = WRITE_BUFSIZE )
size = WRITE_BUFSIZE - 1 ;
if ( copy_from_user ( kbuf , buffer + done , size ) ) {
ret = - EFAULT ;
goto out ;
}
kbuf [ size ] = ' \0 ' ;
tmp = strchr ( kbuf , ' \n ' ) ;
if ( tmp ) {
* tmp = ' \0 ' ;
size = tmp - kbuf + 1 ;
} else if ( done + size < count ) {
2016-03-23 00:28:09 +03:00
pr_warn ( " Line length is too long: Should be less than %d \n " ,
WRITE_BUFSIZE ) ;
2012-04-09 13:11:44 +04:00
ret = - EINVAL ;
goto out ;
}
done + = size ;
/* Remove comments */
tmp = strchr ( kbuf , ' # ' ) ;
if ( tmp )
* tmp = ' \0 ' ;
ret = traceprobe_command ( kbuf , createfn ) ;
if ( ret )
goto out ;
}
ret = done ;
out :
kfree ( kbuf ) ;
return ret ;
}
2013-07-03 11:09:02 +04:00
static int __set_print_fmt ( struct trace_probe * tp , char * buf , int len ,
bool is_return )
{
int i ;
int pos = 0 ;
const char * fmt , * arg ;
if ( ! is_return ) {
fmt = " (%lx) " ;
arg = " REC-> " FIELD_STRING_IP ;
} else {
fmt = " (%lx <- %lx) " ;
arg = " REC-> " FIELD_STRING_FUNC " , REC-> " FIELD_STRING_RETIP ;
}
/* When len=0, we just calculate the needed length */
# define LEN_OR_ZERO (len ? len - pos : 0)
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " \" %s " , fmt ) ;
for ( i = 0 ; i < tp - > nr_args ; i + + ) {
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " %s=%s " ,
tp - > args [ i ] . name , tp - > args [ i ] . type - > fmt ) ;
}
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " \" , %s " , arg ) ;
for ( i = 0 ; i < tp - > nr_args ; i + + ) {
if ( strcmp ( tp - > args [ i ] . type - > name , " string " ) = = 0 )
pos + = snprintf ( buf + pos , LEN_OR_ZERO ,
" , __get_str(%s) " ,
tp - > args [ i ] . name ) ;
else
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " , REC->%s " ,
tp - > args [ i ] . name ) ;
}
# undef LEN_OR_ZERO
/* return the length of print_fmt */
return pos ;
}
int set_print_fmt ( struct trace_probe * tp , bool is_return )
{
int len ;
char * print_fmt ;
/* First: called with 0 length to calculate the needed length */
len = __set_print_fmt ( tp , NULL , 0 , is_return ) ;
print_fmt = kmalloc ( len + 1 , GFP_KERNEL ) ;
if ( ! print_fmt )
return - ENOMEM ;
/* Second: actually write the @print_fmt */
__set_print_fmt ( tp , print_fmt , len + 1 , is_return ) ;
tp - > call . print_fmt = print_fmt ;
return 0 ;
}