2018-08-16 18:23:53 +03:00
// SPDX-License-Identifier: GPL-2.0
2012-04-09 13:11:44 +04:00
/*
* Common code for probe - based Dynamic events .
*
* 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
*/
2017-02-07 14:21:28 +03:00
# define pr_fmt(fmt) "trace_probe: " fmt
2012-04-09 13:11:44 +04:00
# 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) \
2018-04-25 15:16:36 +03:00
int PRINT_TYPE_FUNC_NAME ( tname ) ( struct trace_seq * s , void * data , void * ent ) \
2012-04-09 13:11:44 +04:00
{ \
2018-04-25 15:16:36 +03:00
trace_seq_printf ( s , fmt , * ( type * ) data ) ; \
2014-11-13 01:19:51 +03:00
return ! trace_seq_has_overflowed ( s ) ; \
2012-04-09 13:11:44 +04:00
} \
2018-04-25 15:17:34 +03:00
const char PRINT_TYPE_FMT_NAME ( tname ) [ ] = fmt ;
2012-04-09 13:11:44 +04:00
2016-08-18 11:59:21 +03:00
DEFINE_BASIC_PRINT_TYPE_FUNC ( u8 , u8 , " %u " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( u16 , u16 , " %u " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( u32 , u32 , " %u " )
DEFINE_BASIC_PRINT_TYPE_FUNC ( u64 , u64 , " %Lu " )
2016-08-18 11:57:50 +03:00
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
2018-04-25 15:20:28 +03:00
int PRINT_TYPE_FUNC_NAME ( symbol ) ( struct trace_seq * s , void * data , void * ent )
{
trace_seq_printf ( s , " %pS " , ( void * ) * ( unsigned long * ) data ) ;
return ! trace_seq_has_overflowed ( s ) ;
}
const char PRINT_TYPE_FMT_NAME ( symbol ) [ ] = " %pS " ;
2012-04-09 13:11:44 +04:00
/* Print type function for string type */
2018-04-25 15:16:36 +03:00
int PRINT_TYPE_FUNC_NAME ( string ) ( struct trace_seq * s , void * data , void * ent )
2012-04-09 13:11:44 +04:00
{
int len = * ( u32 * ) data > > 16 ;
if ( ! len )
2018-04-25 15:16:36 +03:00
trace_seq_puts ( s , " (fault) " ) ;
2012-04-09 13:11:44 +04:00
else
2018-04-25 15:16:36 +03:00
trace_seq_printf ( s , " \" %s \" " ,
2014-11-13 01:19:51 +03:00
( const char * ) get_loc_data ( data , ent ) ) ;
return ! trace_seq_has_overflowed ( s ) ;
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
2018-04-25 15:18:32 +03:00
/* Fetch type information table */
static const struct fetch_type probe_fetch_types [ ] = {
/* Special types */
__ASSIGN_FETCH_TYPE ( " string " , string , string , sizeof ( u32 ) , 1 ,
" __data_loc char[] " ) ,
/* Basic types */
ASSIGN_FETCH_TYPE ( u8 , u8 , 0 ) ,
ASSIGN_FETCH_TYPE ( u16 , u16 , 0 ) ,
ASSIGN_FETCH_TYPE ( u32 , u32 , 0 ) ,
ASSIGN_FETCH_TYPE ( u64 , u64 , 0 ) ,
ASSIGN_FETCH_TYPE ( s8 , u8 , 1 ) ,
ASSIGN_FETCH_TYPE ( s16 , u16 , 1 ) ,
ASSIGN_FETCH_TYPE ( s32 , u32 , 1 ) ,
ASSIGN_FETCH_TYPE ( s64 , u64 , 1 ) ,
ASSIGN_FETCH_TYPE_ALIAS ( x8 , u8 , u8 , 0 ) ,
ASSIGN_FETCH_TYPE_ALIAS ( x16 , u16 , u16 , 0 ) ,
ASSIGN_FETCH_TYPE_ALIAS ( x32 , u32 , u32 , 0 ) ,
ASSIGN_FETCH_TYPE_ALIAS ( x64 , u64 , u64 , 0 ) ,
2018-04-25 15:20:28 +03:00
ASSIGN_FETCH_TYPE_ALIAS ( symbol , ADDR_FETCH_TYPE , ADDR_FETCH_TYPE , 0 ) ,
2018-04-25 15:18:32 +03:00
ASSIGN_FETCH_TYPE_END
} ;
static const struct fetch_type * find_fetch_type ( const char * type )
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 :
2018-04-25 15:18:32 +03:00
return find_fetch_type ( " u8 " ) ;
2012-04-09 13:11:44 +04:00
case 16 :
2018-04-25 15:18:32 +03:00
return find_fetch_type ( " u16 " ) ;
2012-04-09 13:11:44 +04:00
case 32 :
2018-04-25 15:18:32 +03:00
return find_fetch_type ( " u32 " ) ;
2012-04-09 13:11:44 +04:00
case 64 :
2018-04-25 15:18:32 +03:00
return find_fetch_type ( " u64 " ) ;
2012-04-09 13:11:44 +04:00
default :
goto fail ;
}
}
2018-04-25 15:18:32 +03:00
for ( i = 0 ; probe_fetch_types [ i ] . name ; i + + ) {
if ( strcmp ( type , probe_fetch_types [ i ] . name ) = = 0 )
return & probe_fetch_types [ i ] ;
2013-11-26 09:56:28 +04:00
}
2012-04-09 13:11:44 +04:00
fail :
return NULL ;
}
/* Split symbol and offset. */
2018-03-17 15:38:10 +03:00
int traceprobe_split_symbol_offset ( char * symbol , long * offset )
2012-04-09 13:11:44 +04:00
{
char * tmp ;
int ret ;
if ( ! offset )
return - EINVAL ;
2018-03-17 15:38:10 +03:00
tmp = strpbrk ( symbol , " +- " ) ;
2012-04-09 13:11:44 +04:00
if ( tmp ) {
2018-03-17 15:38:10 +03:00
ret = kstrtol ( tmp , 0 , offset ) ;
2012-04-09 13:11:44 +04:00
if ( ret )
return ret ;
* tmp = ' \0 ' ;
} else
* offset = 0 ;
return 0 ;
}
2018-11-05 12:02:36 +03:00
/* @buf must has MAX_EVENT_NAME_LEN size */
int traceprobe_parse_event_name ( const char * * pevent , const char * * pgroup ,
char * buf )
{
const char * slash , * event = * pevent ;
slash = strchr ( event , ' / ' ) ;
if ( slash ) {
if ( slash = = event ) {
pr_info ( " Group name is not specified \n " ) ;
return - EINVAL ;
}
if ( slash - event + 1 > MAX_EVENT_NAME_LEN ) {
pr_info ( " Group name is too long \n " ) ;
return - E2BIG ;
}
strlcpy ( buf , event , slash - event + 1 ) ;
* pgroup = buf ;
* pevent = slash + 1 ;
}
if ( strlen ( event ) = = 0 ) {
pr_info ( " Event name is not specified \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2012-04-09 13:11:44 +04:00
# define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
static int parse_probe_vars ( char * arg , const struct fetch_type * t ,
2018-04-25 15:21:26 +03:00
struct fetch_insn * code , unsigned int flags )
2012-04-09 13:11:44 +04:00
{
int ret = 0 ;
unsigned long param ;
if ( strcmp ( arg , " retval " ) = = 0 ) {
2018-04-25 15:21:26 +03:00
if ( flags & TPARG_FL_RETURN )
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_RETVAL ;
2012-04-09 13:11:44 +04:00
else
ret = - EINVAL ;
2018-12-20 21:20:07 +03:00
} else if ( str_has_prefix ( arg , " stack " ) ) {
2012-04-09 13:11:44 +04:00
if ( arg [ 5 ] = = ' \0 ' ) {
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_STACKP ;
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 ) ;
2018-04-25 15:21:26 +03:00
if ( ret | | ( ( flags & TPARG_FL_KERNEL ) & &
param > PARAM_MAX_STACK ) )
2012-04-09 13:11:44 +04:00
ret = - EINVAL ;
else {
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_STACK ;
code - > param = ( unsigned int ) param ;
2012-04-09 13:11:44 +04:00
}
} else
ret = - EINVAL ;
2016-06-09 04:38:02 +03:00
} else if ( strcmp ( arg , " comm " ) = = 0 ) {
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_COMM ;
2018-04-25 15:21:26 +03:00
# ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
} else if ( ( ( flags & TPARG_FL_MASK ) = =
( TPARG_FL_KERNEL | TPARG_FL_FENTRY ) ) & &
2018-12-20 21:20:07 +03:00
str_has_prefix ( arg , " arg " ) ) {
2018-04-25 15:21:26 +03:00
if ( ! isdigit ( arg [ 3 ] ) )
return - EINVAL ;
ret = kstrtoul ( arg + 3 , 10 , & param ) ;
if ( ret | | ! param | | param > PARAM_MAX_STACK )
return - EINVAL ;
code - > op = FETCH_OP_ARG ;
code - > param = ( unsigned int ) param - 1 ;
# endif
2012-04-09 13:11:44 +04:00
} else
ret = - EINVAL ;
return ret ;
}
/* Recursive argument parser */
2018-04-25 15:18:03 +03:00
static int
parse_probe_arg ( char * arg , const struct fetch_type * type ,
struct fetch_insn * * pcode , struct fetch_insn * end ,
2018-04-25 15:21:26 +03:00
unsigned int flags )
2012-04-09 13:11:44 +04:00
{
2018-04-25 15:18:03 +03:00
struct fetch_insn * code = * pcode ;
2012-04-09 13:11:44 +04:00
unsigned long param ;
2018-10-12 19:50:22 +03:00
long offset = 0 ;
2012-04-09 13:11:44 +04:00
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 ' $ ' :
2018-04-25 15:21:26 +03:00
ret = parse_probe_vars ( arg + 1 , type , code , flags ) ;
2012-04-09 13:11:44 +04:00
break ;
case ' % ' : /* named register */
ret = regs_query_register_offset ( arg + 1 ) ;
if ( ret > = 0 ) {
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_REG ;
code - > param = ( unsigned int ) ret ;
2012-04-09 13:11:44 +04:00
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 ;
2018-04-25 15:18:03 +03:00
/* load address */
code - > op = FETCH_OP_IMM ;
code - > immediate = param ;
2013-11-25 08:42:47 +04:00
} else if ( arg [ 1 ] = = ' + ' ) {
/* kprobes don't support file offsets */
2018-04-25 15:21:26 +03:00
if ( flags & TPARG_FL_KERNEL )
2013-11-25 08:42:47 +04:00
return - EINVAL ;
ret = kstrtol ( arg + 2 , 0 , & offset ) ;
if ( ret )
break ;
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_FOFFS ;
code - > immediate = ( unsigned long ) offset ; // imm64?
2012-04-09 13:11:44 +04:00
} else {
2013-07-03 13:34:23 +04:00
/* uprobes don't support symbols */
2018-04-25 15:21:26 +03:00
if ( ! ( flags & TPARG_FL_KERNEL ) )
2013-07-03 13:34:23 +04:00
return - EINVAL ;
2018-08-28 19:18:43 +03:00
/* Preserve symbol for updating */
code - > op = FETCH_NOP_SYMBOL ;
code - > data = kstrdup ( arg + 1 , GFP_KERNEL ) ;
if ( ! code - > data )
return - ENOMEM ;
if ( + + code = = end )
return - E2BIG ;
2012-04-09 13:11:44 +04:00
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_IMM ;
2018-08-28 19:18:43 +03:00
code - > immediate = 0 ;
2012-04-09 13:11:44 +04:00
}
2018-04-25 15:18:03 +03:00
/* These are fetching from memory */
if ( + + code = = end )
return - E2BIG ;
* pcode = code ;
code - > op = FETCH_OP_DEREF ;
code - > offset = offset ;
2012-04-09 13:11:44 +04:00
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 )
2018-04-25 15:18:03 +03:00
return - EINVAL ;
2012-04-09 13:11:44 +04:00
* 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 ) {
2018-04-25 15:18:32 +03:00
const struct fetch_type * t2 = find_fetch_type ( NULL ) ;
2012-04-09 13:11:44 +04:00
* tmp = ' \0 ' ;
2018-04-25 15:21:26 +03:00
ret = parse_probe_arg ( arg , t2 , & code , end , flags ) ;
2012-04-09 13:11:44 +04:00
if ( ret )
2018-04-25 15:18:03 +03:00
break ;
if ( code - > op = = FETCH_OP_COMM )
return - EINVAL ;
if ( + + code = = end )
return - E2BIG ;
* pcode = code ;
code - > op = FETCH_OP_DEREF ;
code - > offset = offset ;
2012-04-09 13:11:44 +04:00
}
break ;
}
2018-04-25 15:18:03 +03:00
if ( ! ret & & code - > op = = FETCH_OP_NOP ) {
/* Parsed, but do not find fetch method */
2012-04-09 13:11:44 +04:00
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 ,
2018-04-25 15:18:03 +03:00
struct fetch_insn * * pcode )
2012-04-09 13:11:44 +04:00
{
2018-04-25 15:18:03 +03:00
struct fetch_insn * code = * pcode ;
2012-04-09 13:11:44 +04:00
unsigned long bw , bo ;
char * tail ;
if ( * bf ! = ' b ' )
return 0 ;
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 ;
2018-04-25 15:18:03 +03:00
code + + ;
if ( code - > op ! = FETCH_OP_NOP )
return - E2BIG ;
* pcode = code ;
2012-04-09 13:11:44 +04:00
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_MOD_BF ;
code - > lshift = BYTES_TO_BITS ( t - > size ) - ( bw + bo ) ;
code - > rshift = BYTES_TO_BITS ( t - > size ) - bw ;
code - > basesize = t - > size ;
2012-04-09 13:11:44 +04:00
return ( BYTES_TO_BITS ( t - > size ) < ( bw + bo ) ) ? - EINVAL : 0 ;
}
/* String length checking wrapper */
2018-11-05 12:01:40 +03:00
static int traceprobe_parse_probe_arg_body ( char * arg , ssize_t * size ,
2018-04-25 15:21:26 +03:00
struct probe_arg * parg , unsigned int flags )
2012-04-09 13:11:44 +04:00
{
2018-04-25 15:21:55 +03:00
struct fetch_insn * code , * scode , * tmp = NULL ;
char * t , * t2 ;
int ret , len ;
2012-04-09 13:11:44 +04:00
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 ;
}
2018-04-25 15:21:55 +03:00
t = strchr ( arg , ' : ' ) ;
2012-04-09 13:11:44 +04:00
if ( t ) {
2018-04-25 15:21:55 +03:00
* t = ' \0 ' ;
t2 = strchr ( + + t , ' [ ' ) ;
if ( t2 ) {
* t2 = ' \0 ' ;
parg - > count = simple_strtoul ( t2 + 1 , & t2 , 0 ) ;
if ( strcmp ( t2 , " ] " ) | | parg - > count = = 0 )
return - EINVAL ;
if ( parg - > count > MAX_ARRAY_LEN )
return - E2BIG ;
}
2012-04-09 13:11:44 +04:00
}
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 )
2018-04-25 15:21:55 +03:00
parg - > type = find_fetch_type ( " string " ) ;
else
parg - > type = find_fetch_type ( t ) ;
2012-04-09 13:11:44 +04:00
if ( ! parg - > type ) {
pr_info ( " Unsupported type: %s \n " , t ) ;
return - EINVAL ;
}
parg - > offset = * size ;
2018-04-25 15:21:55 +03:00
* size + = parg - > type - > size * ( parg - > count ? : 1 ) ;
if ( parg - > count ) {
len = strlen ( parg - > type - > fmttype ) + 6 ;
parg - > fmt = kmalloc ( len , GFP_KERNEL ) ;
if ( ! parg - > fmt )
return - ENOMEM ;
snprintf ( parg - > fmt , len , " %s[%d] " , parg - > type - > fmttype ,
parg - > count ) ;
}
2012-04-09 13:11:44 +04:00
2018-04-25 15:18:03 +03:00
code = tmp = kzalloc ( sizeof ( * code ) * FETCH_INSN_MAX , GFP_KERNEL ) ;
if ( ! code )
return - ENOMEM ;
code [ FETCH_INSN_MAX - 1 ] . op = FETCH_OP_END ;
ret = parse_probe_arg ( arg , parg - > type , & code , & code [ FETCH_INSN_MAX - 1 ] ,
2018-04-25 15:21:26 +03:00
flags ) ;
2018-04-25 15:18:03 +03:00
if ( ret )
goto fail ;
/* Store operation */
if ( ! strcmp ( parg - > type - > name , " string " ) ) {
if ( code - > op ! = FETCH_OP_DEREF & & code - > op ! = FETCH_OP_IMM & &
code - > op ! = FETCH_OP_COMM ) {
pr_info ( " string only accepts memory or address. \n " ) ;
ret = - EINVAL ;
goto fail ;
}
2018-04-25 15:21:55 +03:00
if ( code - > op ! = FETCH_OP_DEREF | | parg - > count ) {
/*
* IMM and COMM is pointing actual address , those must
* be kept , and if parg - > count ! = 0 , this is an array
* of string pointers instead of string address itself .
*/
2018-04-25 15:18:03 +03:00
code + + ;
2018-04-25 15:21:55 +03:00
if ( code - > op ! = FETCH_OP_NOP ) {
ret = - E2BIG ;
goto fail ;
}
}
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_ST_STRING ; /* In DEREF case, replace it */
2018-04-25 15:21:55 +03:00
code - > size = parg - > type - > size ;
2018-04-25 15:18:03 +03:00
parg - > dynamic = true ;
} else if ( code - > op = = FETCH_OP_DEREF ) {
code - > op = FETCH_OP_ST_MEM ;
code - > size = parg - > type - > size ;
} else {
code + + ;
if ( code - > op ! = FETCH_OP_NOP ) {
ret = - E2BIG ;
goto fail ;
}
code - > op = FETCH_OP_ST_RAW ;
code - > size = parg - > type - > size ;
}
2018-04-25 15:21:55 +03:00
scode = code ;
2018-04-25 15:18:03 +03:00
/* Modify operation */
if ( t ! = NULL ) {
ret = __parse_bitfield_probe_arg ( t , parg - > type , & code ) ;
if ( ret )
goto fail ;
2012-04-09 13:11:44 +04:00
}
2018-04-25 15:21:55 +03:00
/* Loop(Array) operation */
if ( parg - > count ) {
if ( scode - > op ! = FETCH_OP_ST_MEM & &
scode - > op ! = FETCH_OP_ST_STRING ) {
pr_info ( " array only accepts memory or address \n " ) ;
ret = - EINVAL ;
goto fail ;
}
code + + ;
if ( code - > op ! = FETCH_OP_NOP ) {
ret = - E2BIG ;
goto fail ;
}
code - > op = FETCH_OP_LP_ARRAY ;
code - > param = parg - > count ;
}
2018-04-25 15:18:03 +03:00
code + + ;
code - > op = FETCH_OP_END ;
/* Shrink down the code buffer */
parg - > code = kzalloc ( sizeof ( * code ) * ( code - tmp + 1 ) , GFP_KERNEL ) ;
if ( ! parg - > code )
ret = - ENOMEM ;
else
memcpy ( parg - > code , tmp , sizeof ( * code ) * ( code - tmp + 1 ) ) ;
fail :
2018-08-28 19:18:43 +03:00
if ( ret ) {
for ( code = tmp ; code < tmp + FETCH_INSN_MAX ; code + + )
if ( code - > op = = FETCH_NOP_SYMBOL )
kfree ( code - > data ) ;
}
2018-04-25 15:18:03 +03:00
kfree ( tmp ) ;
2012-04-09 13:11:44 +04:00
return ret ;
}
/* Return 1 if name is reserved or already used by another argument */
2018-11-05 12:01:40 +03:00
static int traceprobe_conflict_field_name ( const char * name ,
struct probe_arg * args , int narg )
2012-04-09 13:11:44 +04:00
{
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 ;
}
2018-11-05 12:01:40 +03:00
int traceprobe_parse_probe_arg ( struct trace_probe * tp , int i , char * arg ,
unsigned int flags )
{
struct probe_arg * parg = & tp - > args [ i ] ;
char * body ;
int ret ;
/* Increment count for freeing args in error case */
tp - > nr_args + + ;
body = strchr ( arg , ' = ' ) ;
if ( body ) {
parg - > name = kmemdup_nul ( arg , body - arg , GFP_KERNEL ) ;
body + + ;
} else {
/* If argument name is omitted, set "argN" */
parg - > name = kasprintf ( GFP_KERNEL , " arg%d " , i + 1 ) ;
body = arg ;
}
if ( ! parg - > name )
return - ENOMEM ;
if ( ! is_good_name ( parg - > name ) ) {
pr_info ( " Invalid argument[%d] name: %s \n " ,
i , parg - > name ) ;
return - EINVAL ;
}
if ( traceprobe_conflict_field_name ( parg - > name , tp - > args , i ) ) {
pr_info ( " Argument[%d]: '%s' conflicts with another field. \n " ,
i , parg - > name ) ;
return - EINVAL ;
}
/* Parse fetch argument */
ret = traceprobe_parse_probe_arg_body ( body , & tp - > size , parg , flags ) ;
if ( ret )
pr_info ( " Parse error at argument[%d]. (%d) \n " , i , ret ) ;
return ret ;
}
2012-04-09 13:11:44 +04:00
void traceprobe_free_probe_arg ( struct probe_arg * arg )
{
2018-08-28 19:18:43 +03:00
struct fetch_insn * code = arg - > code ;
while ( code & & code - > op ! = FETCH_OP_END ) {
if ( code - > op = = FETCH_NOP_SYMBOL )
kfree ( code - > data ) ;
code + + ;
}
2018-04-25 15:18:03 +03:00
kfree ( arg - > code ) ;
2012-04-09 13:11:44 +04:00
kfree ( arg - > name ) ;
kfree ( arg - > comm ) ;
2018-04-25 15:21:55 +03:00
kfree ( arg - > fmt ) ;
2012-04-09 13:11:44 +04:00
}
2018-08-28 19:18:43 +03:00
int traceprobe_update_arg ( struct probe_arg * arg )
{
struct fetch_insn * code = arg - > code ;
long offset ;
char * tmp ;
char c ;
int ret = 0 ;
while ( code & & code - > op ! = FETCH_OP_END ) {
if ( code - > op = = FETCH_NOP_SYMBOL ) {
if ( code [ 1 ] . op ! = FETCH_OP_IMM )
return - EINVAL ;
2018-11-01 17:29:28 +03:00
tmp = strpbrk ( code - > data , " +- " ) ;
2018-08-28 19:18:43 +03:00
if ( tmp )
c = * tmp ;
ret = traceprobe_split_symbol_offset ( code - > data ,
& offset ) ;
if ( ret )
return ret ;
code [ 1 ] . immediate =
( unsigned long ) kallsyms_lookup_name ( code - > data ) ;
if ( tmp )
* tmp = c ;
if ( ! code [ 1 ] . immediate )
return - ENOENT ;
code [ 1 ] . immediate + = offset ;
}
code + + ;
}
return 0 ;
}
2018-04-25 15:21:55 +03:00
/* When len=0, we just calculate the needed length */
# define LEN_OR_ZERO (len ? len - pos : 0)
2013-07-03 11:09:02 +04:00
static int __set_print_fmt ( struct trace_probe * tp , char * buf , int len ,
bool is_return )
{
2018-04-25 15:21:55 +03:00
struct probe_arg * parg ;
int i , j ;
2013-07-03 11:09:02 +04:00
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 ;
}
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " \" %s " , fmt ) ;
for ( i = 0 ; i < tp - > nr_args ; i + + ) {
2018-04-25 15:21:55 +03:00
parg = tp - > args + i ;
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " %s= " , parg - > name ) ;
if ( parg - > count ) {
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " {%s " ,
parg - > type - > fmt ) ;
for ( j = 1 ; j < parg - > count ; j + + )
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " ,%s " ,
parg - > type - > fmt ) ;
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " } " ) ;
} else
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " %s " ,
parg - > type - > fmt ) ;
2013-07-03 11:09:02 +04:00
}
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " \" , %s " , arg ) ;
for ( i = 0 ; i < tp - > nr_args ; i + + ) {
2018-04-25 15:21:55 +03:00
parg = tp - > args + i ;
if ( parg - > count ) {
if ( strcmp ( parg - > type - > name , " string " ) = = 0 )
fmt = " , __get_str(%s[%d]) " ;
else
fmt = " , REC->%s[%d] " ;
for ( j = 0 ; j < parg - > count ; j + + )
pos + = snprintf ( buf + pos , LEN_OR_ZERO ,
fmt , parg - > name , j ) ;
} else {
if ( strcmp ( parg - > type - > name , " string " ) = = 0 )
fmt = " , __get_str(%s) " ;
else
fmt = " , REC->%s " ;
2013-07-03 11:09:02 +04:00
pos + = snprintf ( buf + pos , LEN_OR_ZERO ,
2018-04-25 15:21:55 +03:00
fmt , parg - > name ) ;
}
2013-07-03 11:09:02 +04:00
}
/* return the length of print_fmt */
return pos ;
}
2018-04-25 15:21:55 +03:00
# undef LEN_OR_ZERO
2013-07-03 11:09:02 +04:00
2018-04-25 15:19:30 +03:00
int traceprobe_set_print_fmt ( struct trace_probe * tp , bool is_return )
2013-07-03 11:09:02 +04:00
{
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 ;
}
2018-04-25 15:17:05 +03:00
int traceprobe_define_arg_fields ( struct trace_event_call * event_call ,
size_t offset , struct trace_probe * tp )
{
int ret , i ;
/* Set argument names as fields */
for ( i = 0 ; i < tp - > nr_args ; i + + ) {
struct probe_arg * parg = & tp - > args [ i ] ;
2018-04-25 15:21:55 +03:00
const char * fmt = parg - > type - > fmttype ;
int size = parg - > type - > size ;
if ( parg - > fmt )
fmt = parg - > fmt ;
if ( parg - > count )
size * = parg - > count ;
ret = trace_define_field ( event_call , fmt , parg - > name ,
offset + parg - > offset , size ,
2018-04-25 15:17:05 +03:00
parg - > type - > is_signed ,
FILTER_OTHER ) ;
if ( ret )
return ret ;
}
return 0 ;
}