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"
2019-04-01 02:48:19 +03:00
# undef C
# define C(a, b) b
static const char * trace_probe_err_text [ ] = { ERRORS } ;
2019-03-12 11:52:58 +03:00
static const char * reserved_field_names [ ] = {
2012-04-09 13:11:44 +04:00
" 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[] " ) ,
2019-05-15 08:38:30 +03:00
__ASSIGN_FETCH_TYPE ( " ustring " , string , string , sizeof ( u32 ) , 1 ,
" __data_loc char[] " ) ,
2018-04-25 15:18:32 +03:00
/* 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 ;
}
2019-04-01 02:48:19 +03:00
static struct trace_probe_log trace_probe_log ;
void trace_probe_log_init ( const char * subsystem , int argc , const char * * argv )
{
trace_probe_log . subsystem = subsystem ;
trace_probe_log . argc = argc ;
trace_probe_log . argv = argv ;
trace_probe_log . index = 0 ;
}
void trace_probe_log_clear ( void )
{
memset ( & trace_probe_log , 0 , sizeof ( trace_probe_log ) ) ;
}
void trace_probe_log_set_index ( int index )
{
trace_probe_log . index = index ;
}
void __trace_probe_log_err ( int offset , int err_type )
{
char * command , * p ;
int i , len = 0 , pos = 0 ;
if ( ! trace_probe_log . argv )
return ;
2021-03-23 20:49:35 +03:00
/* Recalculate the length and allocate buffer */
2019-04-01 02:48:19 +03:00
for ( i = 0 ; i < trace_probe_log . argc ; i + + ) {
if ( i = = trace_probe_log . index )
pos = len ;
len + = strlen ( trace_probe_log . argv [ i ] ) + 1 ;
}
command = kzalloc ( len , GFP_KERNEL ) ;
if ( ! command )
return ;
2019-09-28 12:53:29 +03:00
if ( trace_probe_log . index > = trace_probe_log . argc ) {
/**
* Set the error position is next to the last arg + space .
* Note that len includes the terminal null and the cursor
2021-03-23 20:49:35 +03:00
* appears at pos + 1.
2019-09-28 12:53:29 +03:00
*/
pos = len ;
offset = 0 ;
}
2019-04-01 02:48:19 +03:00
/* And make a command string from argv array */
p = command ;
for ( i = 0 ; i < trace_probe_log . argc ; i + + ) {
len = strlen ( trace_probe_log . argv [ i ] ) ;
strcpy ( p , trace_probe_log . argv [ i ] ) ;
p [ len ] = ' ' ;
p + = len + 1 ;
}
* ( p - 1 ) = ' \0 ' ;
2019-04-02 05:52:21 +03:00
tracing_log_err ( NULL , trace_probe_log . subsystem , command ,
2019-04-01 02:48:19 +03:00
trace_probe_err_text , err_type , pos + offset ) ;
kfree ( command ) ;
}
2012-04-09 13:11:44 +04:00
/* 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 ,
2019-04-01 02:48:19 +03:00
char * buf , int offset )
2018-11-05 12:02:36 +03:00
{
const char * slash , * event = * pevent ;
2019-03-14 07:30:20 +03:00
int len ;
2018-11-05 12:02:36 +03:00
slash = strchr ( event , ' / ' ) ;
2021-08-17 06:42:59 +03:00
if ( ! slash )
slash = strchr ( event , ' . ' ) ;
2018-11-05 12:02:36 +03:00
if ( slash ) {
if ( slash = = event ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset , NO_GROUP_NAME ) ;
2018-11-05 12:02:36 +03:00
return - EINVAL ;
}
if ( slash - event + 1 > MAX_EVENT_NAME_LEN ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset , GROUP_TOO_LONG ) ;
return - EINVAL ;
2018-11-05 12:02:36 +03:00
}
strlcpy ( buf , event , slash - event + 1 ) ;
2019-03-14 07:30:40 +03:00
if ( ! is_good_name ( buf ) ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset , BAD_GROUP_NAME ) ;
2019-03-14 07:30:40 +03:00
return - EINVAL ;
}
2018-11-05 12:02:36 +03:00
* pgroup = buf ;
* pevent = slash + 1 ;
2019-04-01 02:48:19 +03:00
offset + = slash - event + 1 ;
2019-03-14 07:30:20 +03:00
event = * pevent ;
2018-11-05 12:02:36 +03:00
}
2019-03-14 07:30:20 +03:00
len = strlen ( event ) ;
if ( len = = 0 ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset , NO_EVENT_NAME ) ;
2018-11-05 12:02:36 +03:00
return - EINVAL ;
2019-03-14 07:30:20 +03:00
} else if ( len > MAX_EVENT_NAME_LEN ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset , EVENT_TOO_LONG ) ;
return - EINVAL ;
2018-11-05 12:02:36 +03:00
}
2019-03-14 07:30:40 +03:00
if ( ! is_good_name ( event ) ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset , BAD_EVENT_NAME ) ;
2019-03-14 07:30:40 +03:00
return - EINVAL ;
}
2018-11-05 12:02:36 +03:00
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 ,
2019-04-01 02:48:19 +03:00
struct fetch_insn * code , unsigned int flags , int offs )
2012-04-09 13:11:44 +04:00
{
unsigned long param ;
2018-12-22 07:10:26 +03:00
int ret = 0 ;
int len ;
2012-04-09 13:11:44 +04:00
if ( strcmp ( arg , " retval " ) = = 0 ) {
2019-04-01 02:48:19 +03:00
if ( flags & TPARG_FL_RETURN ) {
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_RETVAL ;
2019-04-01 02:48:19 +03:00
} else {
trace_probe_log_err ( offs , RETVAL_ON_PROBE ) ;
2012-04-09 13:11:44 +04:00
ret = - EINVAL ;
2019-04-01 02:48:19 +03:00
}
2018-12-22 07:10:26 +03:00
} else if ( ( len = str_has_prefix ( arg , " stack " ) ) ) {
if ( arg [ len ] = = ' \0 ' ) {
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_STACKP ;
2018-12-22 07:10:26 +03:00
} else if ( isdigit ( arg [ len ] ) ) {
ret = kstrtoul ( arg + len , 10 , & param ) ;
2019-04-01 02:48:19 +03:00
if ( ret ) {
goto inval_var ;
} else if ( ( flags & TPARG_FL_KERNEL ) & &
param > PARAM_MAX_STACK ) {
trace_probe_log_err ( offs , BAD_STACK_NUM ) ;
2012-04-09 13:11:44 +04:00
ret = - EINVAL ;
2019-04-01 02:48:19 +03:00
} 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
2019-04-01 02:48:19 +03:00
goto inval_var ;
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-22 07:10:26 +03:00
( len = str_has_prefix ( arg , " arg " ) ) ) {
ret = kstrtoul ( arg + len , 10 , & param ) ;
2019-04-01 02:48:19 +03:00
if ( ret ) {
goto inval_var ;
} else if ( ! param | | param > PARAM_MAX_STACK ) {
trace_probe_log_err ( offs , BAD_ARG_NUM ) ;
2018-04-25 15:21:26 +03:00
return - EINVAL ;
2019-04-01 02:48:19 +03:00
}
2018-04-25 15:21:26 +03:00
code - > op = FETCH_OP_ARG ;
code - > param = ( unsigned int ) param - 1 ;
# endif
2012-04-09 13:11:44 +04:00
} else
2019-04-01 02:48:19 +03:00
goto inval_var ;
2012-04-09 13:11:44 +04:00
return ret ;
2019-04-01 02:48:19 +03:00
inval_var :
trace_probe_log_err ( offs , BAD_VAR ) ;
return - EINVAL ;
2012-04-09 13:11:44 +04:00
}
2019-06-19 18:08:27 +03:00
static int str_to_immediate ( char * str , unsigned long * imm )
{
if ( isdigit ( str [ 0 ] ) )
return kstrtoul ( str , 0 , imm ) ;
else if ( str [ 0 ] = = ' - ' )
return kstrtol ( str , 0 , ( long * ) imm ) ;
else if ( str [ 0 ] = = ' + ' )
return kstrtol ( str + 1 , 0 , ( long * ) imm ) ;
return - EINVAL ;
}
2019-06-19 18:08:37 +03:00
static int __parse_imm_string ( char * str , char * * pbuf , int offs )
{
size_t len = strlen ( str ) ;
if ( str [ len - 1 ] ! = ' " ' ) {
trace_probe_log_err ( offs + len , IMMSTR_NO_CLOSE ) ;
return - EINVAL ;
}
* pbuf = kstrndup ( str , len - 1 , GFP_KERNEL ) ;
return 0 ;
}
2012-04-09 13:11:44 +04:00
/* 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 ,
2019-04-01 02:48:19 +03:00
unsigned int flags , int offs )
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 ;
2019-05-15 08:38:42 +03:00
int deref = FETCH_OP_DEREF ;
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 ' $ ' :
2019-04-01 02:48:19 +03:00
ret = parse_probe_vars ( arg + 1 , type , code , flags , offs ) ;
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 ;
2019-04-01 02:48:19 +03:00
} else
trace_probe_log_err ( offs , BAD_REG_NAME ) ;
2012-04-09 13:11:44 +04:00
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 ) ;
2019-04-01 02:48:19 +03:00
if ( ret ) {
trace_probe_log_err ( offs , BAD_MEM_ADDR ) ;
2012-04-09 13:11:44 +04:00
break ;
2019-04-01 02:48:19 +03:00
}
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 */
2019-04-01 02:48:19 +03:00
if ( flags & TPARG_FL_KERNEL ) {
trace_probe_log_err ( offs , FILE_ON_KPROBE ) ;
2013-11-25 08:42:47 +04:00
return - EINVAL ;
2019-04-01 02:48:19 +03:00
}
2013-11-25 08:42:47 +04:00
ret = kstrtol ( arg + 2 , 0 , & offset ) ;
2019-04-01 02:48:19 +03:00
if ( ret ) {
trace_probe_log_err ( offs , BAD_FILE_OFFS ) ;
2013-11-25 08:42:47 +04:00
break ;
2019-04-01 02:48:19 +03:00
}
2013-11-25 08:42:47 +04:00
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 */
2019-04-01 02:48:19 +03:00
if ( ! ( flags & TPARG_FL_KERNEL ) ) {
trace_probe_log_err ( offs , SYM_ON_UPROBE ) ;
2013-07-03 13:34:23 +04:00
return - EINVAL ;
2019-04-01 02:48:19 +03:00
}
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 ;
2019-04-01 02:48:19 +03:00
if ( + + code = = end ) {
trace_probe_log_err ( offs , TOO_MANY_OPS ) ;
return - EINVAL ;
}
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 */
2019-04-01 02:48:19 +03:00
if ( + + code = = end ) {
trace_probe_log_err ( offs , TOO_MANY_OPS ) ;
return - EINVAL ;
}
2018-04-25 15:18:03 +03:00
* pcode = code ;
code - > op = FETCH_OP_DEREF ;
code - > offset = offset ;
2012-04-09 13:11:44 +04:00
break ;
case ' + ' : /* deref memory */
case ' - ' :
2019-05-15 08:38:42 +03:00
if ( arg [ 1 ] = = ' u ' ) {
deref = FETCH_OP_UDEREF ;
arg [ 1 ] = arg [ 0 ] ;
arg + + ;
}
if ( arg [ 0 ] = = ' + ' )
arg + + ; /* Skip '+', because kstrtol() rejects it. */
2012-04-09 13:11:44 +04:00
tmp = strchr ( arg , ' ( ' ) ;
2019-04-01 02:48:19 +03:00
if ( ! tmp ) {
trace_probe_log_err ( offs , DEREF_NEED_BRACE ) ;
2018-04-25 15:18:03 +03:00
return - EINVAL ;
2019-04-01 02:48:19 +03:00
}
2012-04-09 13:11:44 +04:00
* tmp = ' \0 ' ;
2012-09-27 00:08:38 +04:00
ret = kstrtol ( arg , 0 , & offset ) ;
2019-04-01 02:48:19 +03:00
if ( ret ) {
trace_probe_log_err ( offs , BAD_DEREF_OFFS ) ;
2012-04-09 13:11:44 +04:00
break ;
2019-04-01 02:48:19 +03:00
}
offs + = ( tmp + 1 - arg ) + ( arg [ 0 ] ! = ' - ' ? 1 : 0 ) ;
2012-04-09 13:11:44 +04:00
arg = tmp + 1 ;
tmp = strrchr ( arg , ' ) ' ) ;
2019-04-01 02:48:19 +03:00
if ( ! tmp ) {
trace_probe_log_err ( offs + strlen ( arg ) ,
DEREF_OPEN_BRACE ) ;
return - EINVAL ;
} else {
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 ' ;
2019-04-01 02:48:19 +03:00
ret = parse_probe_arg ( arg , t2 , & code , end , flags , offs ) ;
2012-04-09 13:11:44 +04:00
if ( ret )
2018-04-25 15:18:03 +03:00
break ;
2019-06-19 18:08:37 +03:00
if ( code - > op = = FETCH_OP_COMM | |
code - > op = = FETCH_OP_DATA ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offs , COMM_CANT_DEREF ) ;
return - EINVAL ;
}
if ( + + code = = end ) {
trace_probe_log_err ( offs , TOO_MANY_OPS ) ;
2018-04-25 15:18:03 +03:00
return - EINVAL ;
2019-04-01 02:48:19 +03:00
}
2018-04-25 15:18:03 +03:00
* pcode = code ;
2019-05-15 08:38:42 +03:00
code - > op = deref ;
2018-04-25 15:18:03 +03:00
code - > offset = offset ;
2012-04-09 13:11:44 +04:00
}
break ;
2019-06-19 18:08:27 +03:00
case ' \\ ' : /* Immediate value */
2019-06-19 18:08:37 +03:00
if ( arg [ 1 ] = = ' " ' ) { /* Immediate string */
ret = __parse_imm_string ( arg + 2 , & tmp , offs + 2 ) ;
if ( ret )
break ;
code - > op = FETCH_OP_DATA ;
code - > data = tmp ;
} else {
ret = str_to_immediate ( arg + 1 , & code - > immediate ) ;
if ( ret )
trace_probe_log_err ( offs + 1 , BAD_IMM ) ;
else
code - > op = FETCH_OP_IMM ;
}
2019-06-19 18:08:27 +03:00
break ;
2012-04-09 13:11:44 +04:00
}
2018-04-25 15:18:03 +03:00
if ( ! ret & & code - > op = = FETCH_OP_NOP ) {
/* Parsed, but do not find fetch method */
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offs , BAD_FETCH_ARG ) ;
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 )
2019-04-01 02:48:19 +03:00
return - EINVAL ;
2018-04-25 15:18:03 +03:00
* 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 */
2021-08-17 06:42:58 +03:00
static int traceprobe_parse_probe_arg_body ( const char * argv , ssize_t * size ,
2019-04-01 02:48:19 +03:00
struct probe_arg * parg , unsigned int flags , int offset )
2012-04-09 13:11:44 +04:00
{
2018-04-25 15:21:55 +03:00
struct fetch_insn * code , * scode , * tmp = NULL ;
2019-04-01 02:48:19 +03:00
char * t , * t2 , * t3 ;
2021-08-17 06:42:58 +03:00
char * arg ;
2018-04-25 15:21:55 +03:00
int ret , len ;
2012-04-09 13:11:44 +04:00
2021-08-17 06:42:58 +03:00
arg = kstrdup ( argv , GFP_KERNEL ) ;
if ( ! arg )
return - ENOMEM ;
ret = - EINVAL ;
2019-04-01 02:48:19 +03:00
len = strlen ( arg ) ;
if ( len > MAX_ARGSTR_LEN ) {
trace_probe_log_err ( offset , ARG_TOO_LONG ) ;
2021-08-17 06:42:58 +03:00
goto out ;
2019-04-01 02:48:19 +03:00
} else if ( len = = 0 ) {
trace_probe_log_err ( offset , NO_ARG_BODY ) ;
2021-08-17 06:42:58 +03:00
goto out ;
2012-04-09 13:11:44 +04:00
}
2019-04-01 02:48:19 +03:00
2021-08-17 06:42:58 +03:00
ret = - ENOMEM ;
2012-04-09 13:11:44 +04:00
parg - > comm = kstrdup ( arg , GFP_KERNEL ) ;
2019-04-01 02:48:19 +03:00
if ( ! parg - > comm )
2021-08-17 06:42:58 +03:00
goto out ;
2019-04-01 02:48:19 +03:00
2021-08-17 06:42:58 +03:00
ret = - EINVAL ;
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 ) {
2019-04-01 02:48:19 +03:00
* t2 + + = ' \0 ' ;
t3 = strchr ( t2 , ' ] ' ) ;
if ( ! t3 ) {
offset + = t2 + strlen ( t2 ) - arg ;
trace_probe_log_err ( offset ,
ARRAY_NO_CLOSE ) ;
2021-08-17 06:42:58 +03:00
goto out ;
2019-04-01 02:48:19 +03:00
} else if ( t3 [ 1 ] ! = ' \0 ' ) {
trace_probe_log_err ( offset + t3 + 1 - arg ,
BAD_ARRAY_SUFFIX ) ;
2021-08-17 06:42:58 +03:00
goto out ;
2019-04-01 02:48:19 +03:00
}
* t3 = ' \0 ' ;
if ( kstrtouint ( t2 , 0 , & parg - > count ) | | ! parg - > count ) {
trace_probe_log_err ( offset + t2 - arg ,
BAD_ARRAY_NUM ) ;
2021-08-17 06:42:58 +03:00
goto out ;
2019-04-01 02:48:19 +03:00
}
if ( parg - > count > MAX_ARRAY_LEN ) {
trace_probe_log_err ( offset + t2 - arg ,
ARRAY_TOO_BIG ) ;
2021-08-17 06:42:58 +03:00
goto out ;
2019-04-01 02:48:19 +03:00
}
2018-04-25 15:21:55 +03:00
}
2012-04-09 13:11:44 +04:00
}
2019-05-07 16:56:02 +03:00
2019-06-19 18:08:37 +03:00
/*
2021-03-23 20:49:35 +03:00
* Since $ comm and immediate string can not be dereferenced ,
2019-06-19 18:08:37 +03:00
* we can find those by strcmp .
*/
if ( strcmp ( arg , " $comm " ) = = 0 | | strncmp ( arg , " \\ \" " , 2 ) = = 0 ) {
2019-05-07 16:56:02 +03:00
/* The type of $comm must be "string", and not an array. */
if ( parg - > count | | ( t & & strcmp ( t , " string " ) ) )
2021-08-17 06:42:58 +03:00
goto out ;
2018-04-25 15:21:55 +03:00
parg - > type = find_fetch_type ( " string " ) ;
2019-05-07 16:56:02 +03:00
} else
2018-04-25 15:21:55 +03:00
parg - > type = find_fetch_type ( t ) ;
2012-04-09 13:11:44 +04:00
if ( ! parg - > type ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset + ( t ? ( t - arg ) : 0 ) , BAD_TYPE ) ;
2021-08-17 06:42:58 +03:00
goto out ;
2012-04-09 13:11:44 +04:00
}
parg - > offset = * size ;
2018-04-25 15:21:55 +03:00
* size + = parg - > type - > size * ( parg - > count ? : 1 ) ;
2021-08-17 06:42:58 +03:00
ret = - ENOMEM ;
2018-04-25 15:21:55 +03:00
if ( parg - > count ) {
len = strlen ( parg - > type - > fmttype ) + 6 ;
parg - > fmt = kmalloc ( len , GFP_KERNEL ) ;
if ( ! parg - > fmt )
2021-08-17 06:42:58 +03:00
goto out ;
2018-04-25 15:21:55 +03:00
snprintf ( parg - > fmt , len , " %s[%d] " , parg - > type - > fmttype ,
parg - > count ) ;
}
2012-04-09 13:11:44 +04:00
2019-01-15 07:34:08 +03:00
code = tmp = kcalloc ( FETCH_INSN_MAX , sizeof ( * code ) , GFP_KERNEL ) ;
2018-04-25 15:18:03 +03:00
if ( ! code )
2021-08-17 06:42:58 +03:00
goto out ;
2018-04-25 15:18:03 +03:00
code [ FETCH_INSN_MAX - 1 ] . op = FETCH_OP_END ;
ret = parse_probe_arg ( arg , parg - > type , & code , & code [ FETCH_INSN_MAX - 1 ] ,
2019-04-01 02:48:19 +03:00
flags , offset ) ;
2018-04-25 15:18:03 +03:00
if ( ret )
goto fail ;
2021-08-17 06:42:58 +03:00
ret = - EINVAL ;
2018-04-25 15:18:03 +03:00
/* Store operation */
2019-05-15 08:38:30 +03:00
if ( ! strcmp ( parg - > type - > name , " string " ) | |
! strcmp ( parg - > type - > name , " ustring " ) ) {
2019-05-15 08:38:42 +03:00
if ( code - > op ! = FETCH_OP_DEREF & & code - > op ! = FETCH_OP_UDEREF & &
2019-06-19 18:08:37 +03:00
code - > op ! = FETCH_OP_IMM & & code - > op ! = FETCH_OP_COMM & &
code - > op ! = FETCH_OP_DATA ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset + ( t ? ( t - arg ) : 0 ) ,
BAD_STRING ) ;
2018-04-25 15:18:03 +03:00
goto fail ;
}
2020-06-15 17:30:38 +03:00
if ( ( code - > op = = FETCH_OP_IMM | | code - > op = = FETCH_OP_COMM | |
code - > op = = FETCH_OP_DATA ) | | parg - > count ) {
2018-04-25 15:21:55 +03:00
/*
2019-06-19 18:08:37 +03:00
* IMM , DATA 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:21:55 +03:00
*/
2018-04-25 15:18:03 +03:00
code + + ;
2018-04-25 15:21:55 +03:00
if ( code - > op ! = FETCH_OP_NOP ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset , TOO_MANY_OPS ) ;
2018-04-25 15:21:55 +03:00
goto fail ;
}
}
2019-05-15 08:38:30 +03:00
/* If op == DEREF, replace it with STRING */
2019-05-15 08:38:42 +03:00
if ( ! strcmp ( parg - > type - > name , " ustring " ) | |
code - > op = = FETCH_OP_UDEREF )
2019-05-15 08:38:30 +03:00
code - > op = FETCH_OP_ST_USTRING ;
else
code - > op = FETCH_OP_ST_STRING ;
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 ;
2019-05-15 08:38:42 +03:00
} else if ( code - > op = = FETCH_OP_UDEREF ) {
code - > op = FETCH_OP_ST_UMEM ;
code - > size = parg - > type - > size ;
2018-04-25 15:18:03 +03:00
} else {
code + + ;
if ( code - > op ! = FETCH_OP_NOP ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset , TOO_MANY_OPS ) ;
2018-04-25 15:18:03 +03:00
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 ) ;
2019-04-01 02:48:19 +03:00
if ( ret ) {
trace_probe_log_err ( offset + t - arg , BAD_BITFIELD ) ;
2018-04-25 15:18:03 +03:00
goto fail ;
2019-04-01 02:48:19 +03:00
}
2012-04-09 13:11:44 +04:00
}
2021-08-17 06:42:58 +03:00
ret = - EINVAL ;
2018-04-25 15:21:55 +03:00
/* Loop(Array) operation */
if ( parg - > count ) {
if ( scode - > op ! = FETCH_OP_ST_MEM & &
2019-05-15 08:38:30 +03:00
scode - > op ! = FETCH_OP_ST_STRING & &
scode - > op ! = FETCH_OP_ST_USTRING ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset + ( t ? ( t - arg ) : 0 ) ,
BAD_STRING ) ;
2018-04-25 15:21:55 +03:00
goto fail ;
}
code + + ;
if ( code - > op ! = FETCH_OP_NOP ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( offset , TOO_MANY_OPS ) ;
2018-04-25 15:21:55 +03:00
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 ;
2021-08-17 06:42:58 +03:00
ret = 0 ;
2018-04-25 15:18:03 +03:00
/* Shrink down the code buffer */
2019-01-15 07:34:08 +03:00
parg - > code = kcalloc ( code - tmp + 1 , sizeof ( * code ) , GFP_KERNEL ) ;
2018-04-25 15:18:03 +03:00
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 + + )
2019-06-19 18:08:37 +03:00
if ( code - > op = = FETCH_NOP_SYMBOL | |
code - > op = = FETCH_OP_DATA )
2018-08-28 19:18:43 +03:00
kfree ( code - > data ) ;
}
2018-04-25 15:18:03 +03:00
kfree ( tmp ) ;
2021-08-17 06:42:58 +03:00
out :
kfree ( arg ) ;
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 ;
}
2021-08-17 06:42:58 +03:00
int traceprobe_parse_probe_arg ( struct trace_probe * tp , int i , const char * arg ,
2018-11-05 12:01:40 +03:00
unsigned int flags )
{
struct probe_arg * parg = & tp - > args [ i ] ;
2021-08-17 06:42:58 +03:00
const char * body ;
2018-11-05 12:01:40 +03:00
/* Increment count for freeing args in error case */
tp - > nr_args + + ;
body = strchr ( arg , ' = ' ) ;
if ( body ) {
2019-04-01 02:48:19 +03:00
if ( body - arg > MAX_ARG_NAME_LEN ) {
trace_probe_log_err ( 0 , ARG_NAME_TOO_LONG ) ;
return - EINVAL ;
} else if ( body = = arg ) {
trace_probe_log_err ( 0 , NO_ARG_NAME ) ;
2019-03-14 07:30:30 +03:00
return - EINVAL ;
2019-04-01 02:48:19 +03:00
}
2018-11-05 12:01:40 +03:00
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 ) ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( 0 , BAD_ARG_NAME ) ;
2018-11-05 12:01:40 +03:00
return - EINVAL ;
}
if ( traceprobe_conflict_field_name ( parg - > name , tp - > args , i ) ) {
2019-04-01 02:48:19 +03:00
trace_probe_log_err ( 0 , USED_ARG_NAME ) ;
2018-11-05 12:01:40 +03:00
return - EINVAL ;
}
/* Parse fetch argument */
2019-04-01 02:48:19 +03:00
return traceprobe_parse_probe_arg_body ( body , & tp - > size , parg , flags ,
body - arg ) ;
2018-11-05 12:01:40 +03:00
}
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 ) {
2019-06-19 18:08:37 +03:00
if ( code - > op = = FETCH_NOP_SYMBOL | |
code - > op = = FETCH_OP_DATA )
2018-08-28 19:18:43 +03:00
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 ,
2021-08-19 07:13:27 +03:00
enum probe_print_type ptype )
2013-07-03 11:09:02 +04:00
{
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 ;
2021-08-19 07:13:27 +03:00
switch ( ptype ) {
case PROBE_PRINT_NORMAL :
2013-07-03 11:09:02 +04:00
fmt = " (%lx) " ;
arg = " REC-> " FIELD_STRING_IP ;
2021-08-19 07:13:27 +03:00
break ;
case PROBE_PRINT_RETURN :
2013-07-03 11:09:02 +04:00
fmt = " (%lx <- %lx) " ;
arg = " REC-> " FIELD_STRING_FUNC " , REC-> " FIELD_STRING_RETIP ;
2021-08-19 07:13:27 +03:00
break ;
default :
WARN_ON_ONCE ( 1 ) ;
return 0 ;
2013-07-03 11:09:02 +04:00
}
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 ) {
2020-01-24 18:07:42 +03:00
if ( ( strcmp ( parg - > type - > name , " string " ) = = 0 ) | |
( strcmp ( parg - > type - > name , " ustring " ) = = 0 ) )
2018-04-25 15:21:55 +03:00
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 {
2020-01-24 18:07:42 +03:00
if ( ( strcmp ( parg - > type - > name , " string " ) = = 0 ) | |
( strcmp ( parg - > type - > name , " ustring " ) = = 0 ) )
2018-04-25 15:21:55 +03:00
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
2021-08-19 07:13:27 +03:00
int traceprobe_set_print_fmt ( struct trace_probe * tp , enum probe_print_type ptype )
2013-07-03 11:09:02 +04:00
{
2019-05-31 18:17:57 +03:00
struct trace_event_call * call = trace_probe_event_call ( tp ) ;
2013-07-03 11:09:02 +04:00
int len ;
char * print_fmt ;
/* First: called with 0 length to calculate the needed length */
2021-08-19 07:13:27 +03:00
len = __set_print_fmt ( tp , NULL , 0 , ptype ) ;
2013-07-03 11:09:02 +04:00
print_fmt = kmalloc ( len + 1 , GFP_KERNEL ) ;
if ( ! print_fmt )
return - ENOMEM ;
/* Second: actually write the @print_fmt */
2021-08-19 07:13:27 +03:00
__set_print_fmt ( tp , print_fmt , len + 1 , ptype ) ;
2019-05-31 18:17:57 +03:00
call - > print_fmt = print_fmt ;
2013-07-03 11:09:02 +04:00
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 ;
}
2019-05-31 18:17:06 +03:00
2019-06-19 18:07:49 +03:00
static void trace_probe_event_free ( struct trace_probe_event * tpe )
{
kfree ( tpe - > class . system ) ;
kfree ( tpe - > call . name ) ;
kfree ( tpe - > call . print_fmt ) ;
kfree ( tpe ) ;
}
int trace_probe_append ( struct trace_probe * tp , struct trace_probe * to )
{
if ( trace_probe_has_sibling ( tp ) )
return - EBUSY ;
list_del_init ( & tp - > list ) ;
trace_probe_event_free ( tp - > event ) ;
tp - > event = to - > event ;
list_add_tail ( & tp - > list , trace_probe_probe_list ( to ) ) ;
return 0 ;
}
void trace_probe_unlink ( struct trace_probe * tp )
{
list_del_init ( & tp - > list ) ;
if ( list_empty ( trace_probe_probe_list ( tp ) ) )
trace_probe_event_free ( tp - > event ) ;
tp - > event = NULL ;
}
2019-05-31 18:17:06 +03:00
void trace_probe_cleanup ( struct trace_probe * tp )
{
int i ;
for ( i = 0 ; i < tp - > nr_args ; i + + )
traceprobe_free_probe_arg ( & tp - > args [ i ] ) ;
2019-06-19 18:07:49 +03:00
if ( tp - > event )
trace_probe_unlink ( tp ) ;
2019-05-31 18:17:06 +03:00
}
int trace_probe_init ( struct trace_probe * tp , const char * event ,
2020-01-22 06:23:25 +03:00
const char * group , bool alloc_filter )
2019-05-31 18:17:06 +03:00
{
2019-06-19 18:07:20 +03:00
struct trace_event_call * call ;
2020-01-22 06:23:25 +03:00
size_t size = sizeof ( struct trace_probe_event ) ;
2019-06-19 18:07:20 +03:00
int ret = 0 ;
2019-05-31 18:17:57 +03:00
2019-05-31 18:17:06 +03:00
if ( ! event | | ! group )
return - EINVAL ;
2020-01-22 06:23:25 +03:00
if ( alloc_filter )
size + = sizeof ( struct trace_uprobe_filter ) ;
tp - > event = kzalloc ( size , GFP_KERNEL ) ;
2019-06-19 18:07:20 +03:00
if ( ! tp - > event )
2019-05-31 18:17:06 +03:00
return - ENOMEM ;
2019-09-17 08:11:37 +03:00
INIT_LIST_HEAD ( & tp - > event - > files ) ;
INIT_LIST_HEAD ( & tp - > event - > class . fields ) ;
INIT_LIST_HEAD ( & tp - > event - > probes ) ;
INIT_LIST_HEAD ( & tp - > list ) ;
2020-05-07 22:30:08 +03:00
list_add ( & tp - > list , & tp - > event - > probes ) ;
2019-09-17 08:11:37 +03:00
2019-06-19 18:07:20 +03:00
call = trace_probe_event_call ( tp ) ;
call - > class = & tp - > event - > class ;
call - > name = kstrdup ( event , GFP_KERNEL ) ;
if ( ! call - > name ) {
ret = - ENOMEM ;
goto error ;
}
tp - > event - > class . system = kstrdup ( group , GFP_KERNEL ) ;
if ( ! tp - > event - > class . system ) {
ret = - ENOMEM ;
goto error ;
2019-05-31 18:17:06 +03:00
}
return 0 ;
2019-06-19 18:07:20 +03:00
error :
trace_probe_cleanup ( tp ) ;
return ret ;
2019-05-31 18:17:06 +03:00
}
2019-05-31 18:17:16 +03:00
2021-08-19 13:26:02 +03:00
static struct trace_event_call *
find_trace_event_call ( const char * system , const char * event_name )
{
struct trace_event_call * tp_event ;
const char * name ;
list_for_each_entry ( tp_event , & ftrace_events , list ) {
if ( ! tp_event - > class - > system | |
strcmp ( system , tp_event - > class - > system ) )
continue ;
name = trace_event_name ( tp_event ) ;
if ( ! name | | strcmp ( event_name , name ) )
continue ;
return tp_event ;
}
return NULL ;
}
2019-05-31 18:17:16 +03:00
int trace_probe_register_event_call ( struct trace_probe * tp )
{
2019-05-31 18:17:57 +03:00
struct trace_event_call * call = trace_probe_event_call ( tp ) ;
2019-05-31 18:17:16 +03:00
int ret ;
2021-08-19 13:26:02 +03:00
lockdep_assert_held ( & event_mutex ) ;
if ( find_trace_event_call ( trace_probe_group_name ( tp ) ,
trace_probe_name ( tp ) ) )
return - EEXIST ;
2019-05-31 18:17:16 +03:00
ret = register_trace_event ( & call - > event ) ;
if ( ! ret )
return - ENODEV ;
ret = trace_add_event_call ( call ) ;
if ( ret )
unregister_trace_event ( & call - > event ) ;
return ret ;
}
2019-05-31 18:17:26 +03:00
int trace_probe_add_file ( struct trace_probe * tp , struct trace_event_file * file )
{
struct event_file_link * link ;
link = kmalloc ( sizeof ( * link ) , GFP_KERNEL ) ;
if ( ! link )
return - ENOMEM ;
link - > file = file ;
INIT_LIST_HEAD ( & link - > list ) ;
2019-06-19 18:07:20 +03:00
list_add_tail_rcu ( & link - > list , & tp - > event - > files ) ;
2019-05-31 18:17:37 +03:00
trace_probe_set_flag ( tp , TP_FLAG_TRACE ) ;
2019-05-31 18:17:26 +03:00
return 0 ;
}
struct event_file_link * trace_probe_get_file_link ( struct trace_probe * tp ,
struct trace_event_file * file )
{
struct event_file_link * link ;
trace_probe_for_each_link ( link , tp ) {
if ( link - > file = = file )
return link ;
}
return NULL ;
}
int trace_probe_remove_file ( struct trace_probe * tp ,
struct trace_event_file * file )
{
struct event_file_link * link ;
link = trace_probe_get_file_link ( tp , file ) ;
if ( ! link )
return - ENOENT ;
list_del_rcu ( & link - > list ) ;
synchronize_rcu ( ) ;
kfree ( link ) ;
2019-06-19 18:07:20 +03:00
if ( list_empty ( & tp - > event - > files ) )
2019-05-31 18:17:37 +03:00
trace_probe_clear_flag ( tp , TP_FLAG_TRACE ) ;
2019-05-31 18:17:26 +03:00
return 0 ;
}
2019-06-19 18:07:49 +03:00
/*
* Return the smallest index of different type argument ( start from 1 ) .
* If all argument types and name are same , return 0.
*/
int trace_probe_compare_arg_type ( struct trace_probe * a , struct trace_probe * b )
{
int i ;
2019-09-28 12:53:29 +03:00
/* In case of more arguments */
if ( a - > nr_args < b - > nr_args )
return a - > nr_args + 1 ;
if ( a - > nr_args > b - > nr_args )
return b - > nr_args + 1 ;
2019-06-19 18:07:49 +03:00
for ( i = 0 ; i < a - > nr_args ; i + + ) {
if ( ( b - > nr_args < = i ) | |
( ( a - > args [ i ] . type ! = b - > args [ i ] . type ) | |
( a - > args [ i ] . count ! = b - > args [ i ] . count ) | |
strcmp ( a - > args [ i ] . name , b - > args [ i ] . name ) ) )
return i + 1 ;
}
return 0 ;
}
2019-06-19 18:08:08 +03:00
bool trace_probe_match_command_args ( struct trace_probe * tp ,
int argc , const char * * argv )
{
char buf [ MAX_ARGSTR_LEN + 1 ] ;
int i ;
if ( tp - > nr_args < argc )
return false ;
for ( i = 0 ; i < argc ; i + + ) {
snprintf ( buf , sizeof ( buf ) , " %s=%s " ,
tp - > args [ i ] . name , tp - > args [ i ] . comm ) ;
if ( strcmp ( buf , argv [ i ] ) )
return false ;
}
return true ;
}
2021-02-01 22:48:11 +03:00
int trace_probe_create ( const char * raw_command , int ( * createfn ) ( int , const char * * ) )
{
int argc = 0 , ret = 0 ;
char * * argv ;
argv = argv_split ( GFP_KERNEL , raw_command , & argc ) ;
if ( ! argv )
return - ENOMEM ;
if ( argc )
ret = createfn ( argc , ( const char * * ) argv ) ;
argv_free ( argv ) ;
return ret ;
}