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
2023-06-06 15:39:56 +03:00
# include <linux/bpf.h>
2023-08-22 19:25:52 +03:00
# include "trace_btf.h"
2023-06-06 15:39:56 +03:00
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 " )
2023-02-21 02:52:42 +03:00
DEFINE_BASIC_PRINT_TYPE_FUNC ( char , u8 , " '%c' " )
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 )
2023-07-11 17:15:57 +03:00
trace_seq_puts ( s , FAULT_STRING ) ;
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 */
2022-11-14 07:47:56 +03:00
__ASSIGN_FETCH_TYPE ( " string " , string , string , sizeof ( u32 ) , 1 , 1 ,
2018-04-25 15:18:32 +03:00
" __data_loc char[] " ) ,
2022-11-14 07:47:56 +03:00
__ASSIGN_FETCH_TYPE ( " ustring " , string , string , sizeof ( u32 ) , 1 , 1 ,
" __data_loc char[] " ) ,
__ASSIGN_FETCH_TYPE ( " symstr " , string , string , sizeof ( u32 ) , 1 , 1 ,
2019-05-15 08:38:30 +03:00
" __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 ) ,
2023-02-21 02:52:42 +03:00
ASSIGN_FETCH_TYPE_ALIAS ( char , u8 , u8 , 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
} ;
2022-11-14 07:47:56 +03:00
static const struct fetch_type * find_fetch_type ( const char * type , unsigned long flags )
2012-04-09 13:11:44 +04:00
{
int i ;
2022-11-14 07:47:56 +03:00
/* Reject the symbol/symstr for uprobes */
if ( type & & ( flags & TPARG_FL_USER ) & &
( ! strcmp ( type , " symbol " ) | | ! strcmp ( type , " symstr " ) ) )
return NULL ;
2012-04-09 13:11:44 +04:00
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 :
2022-11-14 07:47:56 +03:00
return find_fetch_type ( " u8 " , flags ) ;
2012-04-09 13:11:44 +04:00
case 16 :
2022-11-14 07:47:56 +03:00
return find_fetch_type ( " u16 " , flags ) ;
2012-04-09 13:11:44 +04:00
case 32 :
2022-11-14 07:47:56 +03:00
return find_fetch_type ( " u32 " , flags ) ;
2012-04-09 13:11:44 +04:00
case 64 :
2022-11-14 07:47:56 +03:00
return find_fetch_type ( " u64 " , flags ) ;
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
}
2023-05-16 17:39:56 +03:00
strscpy ( buf , event , slash - event + 1 ) ;
2022-11-22 20:23:45 +03:00
if ( ! is_good_system_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 ) {
2022-06-27 05:19:07 +03:00
if ( slash ) {
* pevent = NULL ;
return 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 ;
}
2023-06-06 15:39:56 +03:00
static int parse_trace_event_arg ( char * arg , struct fetch_insn * code ,
struct traceprobe_parse_context * ctx )
{
struct ftrace_event_field * field ;
struct list_head * head ;
head = trace_get_fields ( ctx - > event ) ;
list_for_each_entry ( field , head , link ) {
if ( ! strcmp ( arg , field - > name ) ) {
code - > op = FETCH_OP_TP_ARG ;
code - > data = field ;
return 0 ;
}
}
return - ENOENT ;
}
2023-06-06 15:39:56 +03:00
# ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
static u32 btf_type_int ( const struct btf_type * t )
{
return * ( u32 * ) ( t + 1 ) ;
}
2023-08-22 19:26:32 +03:00
static bool btf_type_is_char_ptr ( struct btf * btf , const struct btf_type * type )
{
const struct btf_type * real_type ;
u32 intdata ;
s32 tid ;
real_type = btf_type_skip_modifiers ( btf , type - > type , & tid ) ;
if ( ! real_type )
return false ;
if ( BTF_INFO_KIND ( real_type - > info ) ! = BTF_KIND_INT )
return false ;
intdata = btf_type_int ( real_type ) ;
return ! ( BTF_INT_ENCODING ( intdata ) & BTF_INT_SIGNED )
& & BTF_INT_BITS ( intdata ) = = 8 ;
}
static bool btf_type_is_char_array ( struct btf * btf , const struct btf_type * type )
{
const struct btf_type * real_type ;
const struct btf_array * array ;
u32 intdata ;
s32 tid ;
if ( BTF_INFO_KIND ( type - > info ) ! = BTF_KIND_ARRAY )
return false ;
array = ( const struct btf_array * ) ( type + 1 ) ;
real_type = btf_type_skip_modifiers ( btf , array - > type , & tid ) ;
intdata = btf_type_int ( real_type ) ;
return ! ( BTF_INT_ENCODING ( intdata ) & BTF_INT_SIGNED )
& & BTF_INT_BITS ( intdata ) = = 8 ;
}
static int check_prepare_btf_string_fetch ( char * typename ,
struct fetch_insn * * pcode ,
struct traceprobe_parse_context * ctx )
{
struct btf * btf = ctx - > btf ;
if ( ! btf | | ! ctx - > last_type )
return 0 ;
/* char [] does not need any change. */
if ( btf_type_is_char_array ( btf , ctx - > last_type ) )
return 0 ;
/* char * requires dereference the pointer. */
if ( btf_type_is_char_ptr ( btf , ctx - > last_type ) ) {
struct fetch_insn * code = * pcode + 1 ;
if ( code - > op = = FETCH_OP_END ) {
trace_probe_log_err ( ctx - > offset , TOO_MANY_OPS ) ;
return - E2BIG ;
}
if ( typename [ 0 ] = = ' u ' )
code - > op = FETCH_OP_UDEREF ;
else
code - > op = FETCH_OP_DEREF ;
code - > offset = 0 ;
* pcode = code ;
return 0 ;
}
/* Other types are not available for string */
trace_probe_log_err ( ctx - > offset , BAD_TYPE4STR ) ;
return - EINVAL ;
}
2023-08-22 19:26:12 +03:00
static const char * fetch_type_from_btf_type ( struct btf * btf ,
const struct btf_type * type ,
struct traceprobe_parse_context * ctx )
2023-06-06 15:39:56 +03:00
{
u32 intdata ;
/* TODO: const char * could be converted as a string */
2023-08-22 19:26:12 +03:00
switch ( BTF_INFO_KIND ( type - > info ) ) {
2023-06-06 15:39:56 +03:00
case BTF_KIND_ENUM :
/* enum is "int", so convert to "s32" */
return " s32 " ;
case BTF_KIND_ENUM64 :
return " s64 " ;
case BTF_KIND_PTR :
/* pointer will be converted to "x??" */
if ( IS_ENABLED ( CONFIG_64BIT ) )
return " x64 " ;
else
return " x32 " ;
case BTF_KIND_INT :
2023-08-22 19:26:12 +03:00
intdata = btf_type_int ( type ) ;
2023-06-06 15:39:56 +03:00
if ( BTF_INT_ENCODING ( intdata ) & BTF_INT_SIGNED ) {
switch ( BTF_INT_BITS ( intdata ) ) {
case 8 :
return " s8 " ;
case 16 :
return " s16 " ;
case 32 :
return " s32 " ;
case 64 :
return " s64 " ;
}
} else { /* unsigned */
switch ( BTF_INT_BITS ( intdata ) ) {
case 8 :
return " u8 " ;
case 16 :
return " u16 " ;
case 32 :
return " u32 " ;
case 64 :
return " u64 " ;
}
2023-08-22 19:26:12 +03:00
/* bitfield, size is encoded in the type */
ctx - > last_bitsize = BTF_INT_BITS ( intdata ) ;
ctx - > last_bitoffs + = BTF_INT_OFFSET ( intdata ) ;
return " u64 " ;
2023-06-06 15:39:56 +03:00
}
}
/* TODO: support other types */
return NULL ;
}
2023-08-22 19:26:22 +03:00
static int query_btf_context ( struct traceprobe_parse_context * ctx )
2023-06-06 15:39:56 +03:00
{
const struct btf_param * param ;
2023-08-22 19:26:22 +03:00
const struct btf_type * type ;
2023-08-22 19:25:42 +03:00
struct btf * btf ;
2023-08-22 19:26:22 +03:00
s32 nr ;
2023-06-06 15:39:56 +03:00
2023-08-22 19:26:22 +03:00
if ( ctx - > btf )
return 0 ;
2023-06-06 15:39:56 +03:00
2023-08-22 19:26:22 +03:00
if ( ! ctx - > funcname )
return - EINVAL ;
2023-06-06 15:39:56 +03:00
2023-08-22 19:26:22 +03:00
type = btf_find_func_proto ( ctx - > funcname , & btf ) ;
if ( ! type )
return - ENOENT ;
2023-06-06 15:39:56 +03:00
2023-08-22 19:26:22 +03:00
ctx - > btf = btf ;
ctx - > proto = type ;
/* ctx->params is optional, since func(void) will not have params. */
nr = 0 ;
param = btf_get_func_param ( type , & nr ) ;
if ( ! IS_ERR_OR_NULL ( param ) ) {
/* Hide the first 'data' argument of tracepoint */
if ( ctx - > flags & TPARG_FL_TPOINT ) {
nr - - ;
param + + ;
}
2023-06-06 15:39:56 +03:00
}
2023-08-22 19:26:22 +03:00
if ( nr > 0 ) {
ctx - > nr_params = nr ;
ctx - > params = param ;
} else {
ctx - > nr_params = 0 ;
ctx - > params = NULL ;
2023-08-22 19:25:42 +03:00
}
2023-08-22 19:26:22 +03:00
return 0 ;
2023-08-22 19:25:42 +03:00
}
static void clear_btf_context ( struct traceprobe_parse_context * ctx )
{
if ( ctx - > btf ) {
btf_put ( ctx - > btf ) ;
ctx - > btf = NULL ;
2023-08-22 19:26:22 +03:00
ctx - > proto = NULL ;
2023-08-22 19:25:42 +03:00
ctx - > params = NULL ;
ctx - > nr_params = 0 ;
}
2023-06-06 15:39:56 +03:00
}
2023-08-22 19:26:12 +03:00
/* Return 1 if the field separater is arrow operator ('->') */
static int split_next_field ( char * varname , char * * next_field ,
struct traceprobe_parse_context * ctx )
{
char * field ;
int ret = 0 ;
field = strpbrk ( varname , " .- " ) ;
if ( field ) {
if ( field [ 0 ] = = ' - ' & & field [ 1 ] = = ' > ' ) {
field [ 0 ] = ' \0 ' ;
field + = 2 ;
ret = 1 ;
} else if ( field [ 0 ] = = ' . ' ) {
field [ 0 ] = ' \0 ' ;
field + = 1 ;
} else {
trace_probe_log_err ( ctx - > offset + field - varname , BAD_HYPHEN ) ;
return - EINVAL ;
}
* next_field = field ;
}
return ret ;
}
/*
* Parse the field of data structure . The @ type must be a pointer type
* pointing the target data structure type .
*/
static int parse_btf_field ( char * fieldname , const struct btf_type * type ,
struct fetch_insn * * pcode , struct fetch_insn * end ,
struct traceprobe_parse_context * ctx )
{
struct fetch_insn * code = * pcode ;
const struct btf_member * field ;
u32 bitoffs , anon_offs ;
char * next ;
int is_ptr ;
s32 tid ;
do {
/* Outer loop for solving arrow operator ('->') */
if ( BTF_INFO_KIND ( type - > info ) ! = BTF_KIND_PTR ) {
trace_probe_log_err ( ctx - > offset , NO_PTR_STRCT ) ;
return - EINVAL ;
}
/* Convert a struct pointer type to a struct type */
type = btf_type_skip_modifiers ( ctx - > btf , type - > type , & tid ) ;
if ( ! type ) {
trace_probe_log_err ( ctx - > offset , BAD_BTF_TID ) ;
return - EINVAL ;
}
bitoffs = 0 ;
do {
/* Inner loop for solving dot operator ('.') */
next = NULL ;
is_ptr = split_next_field ( fieldname , & next , ctx ) ;
if ( is_ptr < 0 )
return is_ptr ;
anon_offs = 0 ;
field = btf_find_struct_member ( ctx - > btf , type , fieldname ,
& anon_offs ) ;
if ( ! field ) {
trace_probe_log_err ( ctx - > offset , NO_BTF_FIELD ) ;
return - ENOENT ;
}
/* Add anonymous structure/union offset */
bitoffs + = anon_offs ;
/* Accumulate the bit-offsets of the dot-connected fields */
if ( btf_type_kflag ( type ) ) {
bitoffs + = BTF_MEMBER_BIT_OFFSET ( field - > offset ) ;
ctx - > last_bitsize = BTF_MEMBER_BITFIELD_SIZE ( field - > offset ) ;
} else {
bitoffs + = field - > offset ;
ctx - > last_bitsize = 0 ;
}
type = btf_type_skip_modifiers ( ctx - > btf , field - > type , & tid ) ;
if ( ! type ) {
trace_probe_log_err ( ctx - > offset , BAD_BTF_TID ) ;
return - EINVAL ;
}
ctx - > offset + = next - fieldname ;
fieldname = next ;
} while ( ! is_ptr & & fieldname ) ;
if ( + + code = = end ) {
trace_probe_log_err ( ctx - > offset , TOO_MANY_OPS ) ;
return - EINVAL ;
}
code - > op = FETCH_OP_DEREF ; /* TODO: user deref support */
code - > offset = bitoffs / 8 ;
* pcode = code ;
ctx - > last_bitoffs = bitoffs % 8 ;
ctx - > last_type = type ;
} while ( fieldname ) ;
return 0 ;
}
static int parse_btf_arg ( char * varname ,
struct fetch_insn * * pcode , struct fetch_insn * end ,
2023-06-06 15:39:56 +03:00
struct traceprobe_parse_context * ctx )
{
2023-08-22 19:26:12 +03:00
struct fetch_insn * code = * pcode ;
2023-06-06 15:39:56 +03:00
const struct btf_param * params ;
2023-08-22 19:26:12 +03:00
const struct btf_type * type ;
char * field = NULL ;
2023-08-22 19:26:22 +03:00
int i , is_ptr , ret ;
2023-08-22 19:26:12 +03:00
u32 tid ;
2023-06-06 15:39:56 +03:00
if ( WARN_ON_ONCE ( ! ctx - > funcname ) )
return - EINVAL ;
2023-08-22 19:26:12 +03:00
is_ptr = split_next_field ( varname , & field , ctx ) ;
if ( is_ptr < 0 )
return is_ptr ;
if ( ! is_ptr & & field ) {
/* dot-connected field on an argument is not supported. */
trace_probe_log_err ( ctx - > offset + field - varname ,
NOSUP_DAT_ARG ) ;
return - EOPNOTSUPP ;
}
2023-08-22 19:26:22 +03:00
if ( ctx - > flags & TPARG_FL_RETURN ) {
if ( strcmp ( varname , " $retval " ) ! = 0 ) {
trace_probe_log_err ( ctx - > offset , NO_BTFARG ) ;
return - ENOENT ;
}
code - > op = FETCH_OP_RETVAL ;
/* Check whether the function return type is not void */
if ( query_btf_context ( ctx ) = = 0 ) {
if ( ctx - > proto - > type = = 0 ) {
trace_probe_log_err ( ctx - > offset , NO_RETVAL ) ;
return - ENOENT ;
}
tid = ctx - > proto - > type ;
goto found ;
}
if ( field ) {
trace_probe_log_err ( ctx - > offset + field - varname ,
NO_BTF_ENTRY ) ;
return - ENOENT ;
}
return 0 ;
}
if ( ! ctx - > btf ) {
ret = query_btf_context ( ctx ) ;
if ( ret < 0 | | ctx - > nr_params = = 0 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , NO_BTF_ENTRY ) ;
return PTR_ERR ( params ) ;
}
2023-08-22 19:26:22 +03:00
}
params = ctx - > params ;
2023-06-06 15:39:56 +03:00
for ( i = 0 ; i < ctx - > nr_params ; i + + ) {
2023-08-22 19:25:42 +03:00
const char * name = btf_name_by_offset ( ctx - > btf , params [ i ] . name_off ) ;
2023-06-06 15:39:56 +03:00
if ( name & & ! strcmp ( name , varname ) ) {
code - > op = FETCH_OP_ARG ;
2023-06-12 14:58:57 +03:00
if ( ctx - > flags & TPARG_FL_TPOINT )
code - > param = i + 1 ;
else
code - > param = i ;
2023-08-22 19:26:12 +03:00
tid = params [ i ] . type ;
goto found ;
2023-06-06 15:39:56 +03:00
}
}
trace_probe_log_err ( ctx - > offset , NO_BTFARG ) ;
return - ENOENT ;
2023-08-22 19:26:12 +03:00
found :
type = btf_type_skip_modifiers ( ctx - > btf , tid , & tid ) ;
if ( ! type ) {
trace_probe_log_err ( ctx - > offset , BAD_BTF_TID ) ;
return - EINVAL ;
}
/* Initialize the last type information */
ctx - > last_type = type ;
ctx - > last_bitoffs = 0 ;
ctx - > last_bitsize = 0 ;
if ( field ) {
ctx - > offset + = field - varname ;
return parse_btf_field ( field , type , pcode , end , ctx ) ;
}
return 0 ;
2023-06-06 15:39:56 +03:00
}
2023-08-22 19:26:22 +03:00
static const struct fetch_type * find_fetch_type_from_btf_type (
2023-06-06 15:39:56 +03:00
struct traceprobe_parse_context * ctx )
{
2023-08-22 19:25:42 +03:00
struct btf * btf = ctx - > btf ;
2023-06-06 15:39:56 +03:00
const char * typestr = NULL ;
2023-08-22 19:26:12 +03:00
if ( btf & & ctx - > last_type )
typestr = fetch_type_from_btf_type ( btf , ctx - > last_type , ctx ) ;
2023-06-06 15:39:56 +03:00
return find_fetch_type ( typestr , ctx - > flags ) ;
}
2023-06-06 15:39:56 +03:00
2023-08-22 19:26:12 +03:00
static int parse_btf_bitfield ( struct fetch_insn * * pcode ,
struct traceprobe_parse_context * ctx )
{
struct fetch_insn * code = * pcode ;
if ( ( ctx - > last_bitsize % 8 = = 0 ) & & ctx - > last_bitoffs = = 0 )
return 0 ;
code + + ;
if ( code - > op ! = FETCH_OP_NOP ) {
trace_probe_log_err ( ctx - > offset , TOO_MANY_OPS ) ;
return - EINVAL ;
}
* pcode = code ;
code - > op = FETCH_OP_MOD_BF ;
code - > lshift = 64 - ( ctx - > last_bitsize + ctx - > last_bitoffs ) ;
code - > rshift = 64 - ctx - > last_bitsize ;
code - > basesize = 64 / 8 ;
return 0 ;
}
2023-06-06 15:39:56 +03:00
# else
2023-08-22 19:25:42 +03:00
static void clear_btf_context ( struct traceprobe_parse_context * ctx )
2023-06-06 15:39:56 +03:00
{
2023-08-22 19:25:42 +03:00
ctx - > btf = NULL ;
2023-06-06 15:39:56 +03:00
}
2023-08-22 19:26:22 +03:00
static int query_btf_context ( struct traceprobe_parse_context * ctx )
2023-06-06 15:39:56 +03:00
{
2023-08-22 19:26:22 +03:00
return - EOPNOTSUPP ;
2023-06-06 15:39:56 +03:00
}
2023-08-22 19:26:12 +03:00
static int parse_btf_arg ( char * varname ,
struct fetch_insn * * pcode , struct fetch_insn * end ,
2023-06-06 15:39:56 +03:00
struct traceprobe_parse_context * ctx )
{
trace_probe_log_err ( ctx - > offset , NOSUP_BTFARG ) ;
return - EOPNOTSUPP ;
}
2023-06-06 15:39:56 +03:00
2023-08-22 19:26:12 +03:00
static int parse_btf_bitfield ( struct fetch_insn * * pcode ,
struct traceprobe_parse_context * ctx )
{
trace_probe_log_err ( ctx - > offset , NOSUP_BTFARG ) ;
return - EOPNOTSUPP ;
}
2023-08-22 19:26:22 +03:00
# define find_fetch_type_from_btf_type(ctx) \
2023-06-06 15:39:56 +03:00
find_fetch_type ( NULL , ctx - > flags )
2023-06-06 15:39:56 +03:00
2023-08-22 19:26:32 +03:00
static int check_prepare_btf_string_fetch ( char * typename ,
struct fetch_insn * * pcode ,
struct traceprobe_parse_context * ctx )
{
return 0 ;
}
2023-06-06 15:39:56 +03:00
# endif
2012-04-09 13:11:44 +04:00
# define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
2023-08-22 19:26:22 +03:00
/* Parse $vars. @orig_arg points '$', which syncs to @ctx->offset */
static int parse_probe_vars ( char * orig_arg , const struct fetch_type * t ,
struct fetch_insn * * pcode ,
struct fetch_insn * end ,
2023-06-06 15:39:56 +03:00
struct traceprobe_parse_context * ctx )
2012-04-09 13:11:44 +04:00
{
2023-08-22 19:26:22 +03:00
struct fetch_insn * code = * pcode ;
2023-06-06 15:39:56 +03:00
int err = TP_ERR_BAD_VAR ;
2023-08-22 19:26:22 +03:00
char * arg = orig_arg + 1 ;
unsigned long param ;
2018-12-22 07:10:26 +03:00
int ret = 0 ;
int len ;
2012-04-09 13:11:44 +04:00
2023-06-06 15:39:56 +03:00
if ( ctx - > flags & TPARG_FL_TEVENT ) {
2022-08-20 16:43:17 +03:00
if ( code - > data )
return - EFAULT ;
2023-06-06 15:39:56 +03:00
ret = parse_trace_event_arg ( arg , code , ctx ) ;
if ( ! ret )
return 0 ;
if ( strcmp ( arg , " comm " ) = = 0 | | strcmp ( arg , " COMM " ) = = 0 ) {
code - > op = FETCH_OP_COMM ;
return 0 ;
}
/* backward compatibility */
ctx - > offset = 0 ;
goto inval ;
}
2023-08-22 19:26:22 +03:00
if ( str_has_prefix ( arg , " retval " ) ) {
if ( ! ( ctx - > flags & TPARG_FL_RETURN ) ) {
err = TP_ERR_RETVAL_ON_PROBE ;
goto inval ;
}
if ( ! ( ctx - > flags & TPARG_FL_KERNEL ) | |
! IS_ENABLED ( CONFIG_PROBE_EVENTS_BTF_ARGS ) ) {
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_RETVAL ;
2023-06-06 15:39:56 +03:00
return 0 ;
2019-04-01 02:48:19 +03:00
}
2023-08-22 19:26:22 +03:00
return parse_btf_arg ( orig_arg , pcode , end , ctx ) ;
2023-06-06 15:39:56 +03:00
}
len = str_has_prefix ( arg , " stack " ) ;
if ( len ) {
2018-12-22 07:10:26 +03:00
if ( arg [ len ] = = ' \0 ' ) {
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_STACKP ;
2023-06-06 15:39:56 +03:00
return 0 ;
}
if ( isdigit ( arg [ len ] ) ) {
2018-12-22 07:10:26 +03:00
ret = kstrtoul ( arg + len , 10 , & param ) ;
2023-06-06 15:39:56 +03:00
if ( ret )
goto inval ;
if ( ( ctx - > flags & TPARG_FL_KERNEL ) & &
param > PARAM_MAX_STACK ) {
err = TP_ERR_BAD_STACK_NUM ;
goto inval ;
2012-04-09 13:11:44 +04:00
}
2023-06-06 15:39:56 +03:00
code - > op = FETCH_OP_STACK ;
code - > param = ( unsigned int ) param ;
return 0 ;
}
goto inval ;
}
if ( strcmp ( arg , " comm " ) = = 0 | | strcmp ( arg , " COMM " ) = = 0 ) {
2018-04-25 15:18:03 +03:00
code - > op = FETCH_OP_COMM ;
2023-06-06 15:39:56 +03:00
return 0 ;
}
2018-04-25 15:21:26 +03:00
# ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
2023-06-06 15:39:56 +03:00
len = str_has_prefix ( arg , " arg " ) ;
if ( len & & tparg_is_function_entry ( ctx - > flags ) ) {
2018-12-22 07:10:26 +03:00
ret = kstrtoul ( arg + len , 10 , & param ) ;
2023-06-06 15:39:56 +03:00
if ( ret )
goto inval ;
if ( ! param | | param > PARAM_MAX_STACK ) {
err = TP_ERR_BAD_ARG_NUM ;
goto inval ;
2019-04-01 02:48:19 +03:00
}
2023-06-06 15:39:56 +03:00
2018-04-25 15:21:26 +03:00
code - > op = FETCH_OP_ARG ;
code - > param = ( unsigned int ) param - 1 ;
2023-06-06 15:39:55 +03:00
/*
* The tracepoint probe will probe a stub function , and the
* first parameter of the stub is a dummy and should be ignored .
*/
2023-06-06 15:39:56 +03:00
if ( ctx - > flags & TPARG_FL_TPOINT )
2023-06-06 15:39:55 +03:00
code - > param + + ;
2023-06-06 15:39:56 +03:00
return 0 ;
}
2018-04-25 15:21:26 +03:00
# endif
2019-04-01 02:48:19 +03:00
2023-06-06 15:39:56 +03:00
inval :
__trace_probe_log_err ( ctx - > offset , err ) ;
2019-04-01 02:48:19 +03:00
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 ) ;
2021-12-14 05:26:46 +03:00
if ( ! * pbuf )
return - ENOMEM ;
2019-06-19 18:08:37 +03:00
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 ,
2023-06-06 15:39:56 +03:00
struct traceprobe_parse_context * ctx )
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 ' $ ' :
2023-08-22 19:26:22 +03:00
ret = parse_probe_vars ( arg , type , pcode , end , ctx ) ;
2012-04-09 13:11:44 +04:00
break ;
case ' % ' : /* named register */
2023-06-06 15:39:56 +03:00
if ( ctx - > flags & ( TPARG_FL_TEVENT | TPARG_FL_FPROBE ) ) {
2023-06-06 15:39:55 +03:00
/* eprobe and fprobe do not handle registers */
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , BAD_VAR ) ;
2022-08-20 16:43:17 +03:00
break ;
}
2012-04-09 13:11:44 +04:00
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
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , 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 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , 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 */
2023-06-06 15:39:56 +03:00
if ( ctx - > flags & TPARG_FL_KERNEL ) {
trace_probe_log_err ( ctx - > offset , 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 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , 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 */
2023-06-06 15:39:56 +03:00
if ( ! ( ctx - > flags & TPARG_FL_KERNEL ) ) {
trace_probe_log_err ( ctx - > offset , 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 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , TOO_MANY_OPS ) ;
2019-04-01 02:48:19 +03:00
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 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , TOO_MANY_OPS ) ;
2019-04-01 02:48:19 +03:00
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 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , 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 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , BAD_DEREF_OFFS ) ;
2012-04-09 13:11:44 +04:00
break ;
2019-04-01 02:48:19 +03:00
}
2023-06-06 15:39:56 +03:00
ctx - > offset + = ( 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 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset + strlen ( arg ) ,
2019-04-01 02:48:19 +03:00
DEREF_OPEN_BRACE ) ;
return - EINVAL ;
} else {
2023-06-06 15:39:56 +03:00
const struct fetch_type * t2 = find_fetch_type ( NULL , ctx - > flags ) ;
int cur_offs = ctx - > offset ;
2012-04-09 13:11:44 +04:00
* tmp = ' \0 ' ;
2023-06-06 15:39:56 +03:00
ret = parse_probe_arg ( arg , t2 , & code , end , ctx ) ;
2012-04-09 13:11:44 +04:00
if ( ret )
2018-04-25 15:18:03 +03:00
break ;
2023-06-06 15:39:56 +03:00
ctx - > offset = cur_offs ;
2019-06-19 18:08:37 +03:00
if ( code - > op = = FETCH_OP_COMM | |
code - > op = = FETCH_OP_DATA ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , COMM_CANT_DEREF ) ;
2019-04-01 02:48:19 +03:00
return - EINVAL ;
}
if ( + + code = = end ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , 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 ;
2023-08-22 19:26:12 +03:00
/* Reset the last type if used */
ctx - > last_type = NULL ;
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 */
2023-06-06 15:39:56 +03:00
ret = __parse_imm_string ( arg + 2 , & tmp , ctx - > offset + 2 ) ;
2019-06-19 18:08:37 +03:00
if ( ret )
break ;
code - > op = FETCH_OP_DATA ;
code - > data = tmp ;
} else {
ret = str_to_immediate ( arg + 1 , & code - > immediate ) ;
if ( ret )
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset + 1 , BAD_IMM ) ;
2019-06-19 18:08:37 +03:00
else
code - > op = FETCH_OP_IMM ;
}
2019-06-19 18:08:27 +03:00
break ;
2023-06-06 15:39:56 +03:00
default :
if ( isalpha ( arg [ 0 ] ) | | arg [ 0 ] = = ' _ ' ) { /* BTF variable */
if ( ! tparg_is_function_entry ( ctx - > flags ) ) {
trace_probe_log_err ( ctx - > offset , NOSUP_BTFARG ) ;
return - EINVAL ;
}
2023-08-22 19:26:12 +03:00
ret = parse_btf_arg ( arg , pcode , end , ctx ) ;
2023-06-06 15:39:56 +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 */
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , 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 ;
}
2024-03-04 06:40:26 +03:00
/* Split type part from @arg and return it. */
static char * parse_probe_arg_type ( char * arg , struct probe_arg * parg ,
struct traceprobe_parse_context * ctx )
2012-04-09 13:11:44 +04:00
{
2024-03-04 06:40:26 +03:00
char * t = NULL , * t2 , * t3 ;
int offs ;
2019-04-01 02:48:19 +03:00
2018-04-25 15:21:55 +03:00
t = strchr ( arg , ' : ' ) ;
2012-04-09 13:11:44 +04:00
if ( t ) {
2024-03-04 06:40:26 +03:00
* t + + = ' \0 ' ;
t2 = strchr ( t , ' [ ' ) ;
2018-04-25 15:21:55 +03:00
if ( t2 ) {
2019-04-01 02:48:19 +03:00
* t2 + + = ' \0 ' ;
t3 = strchr ( t2 , ' ] ' ) ;
if ( ! t3 ) {
2024-03-04 06:40:26 +03:00
offs = t2 + strlen ( t2 ) - arg ;
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset + offs ,
2019-04-01 02:48:19 +03:00
ARRAY_NO_CLOSE ) ;
2024-03-04 06:40:26 +03:00
return ERR_PTR ( - EINVAL ) ;
2019-04-01 02:48:19 +03:00
} else if ( t3 [ 1 ] ! = ' \0 ' ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset + t3 + 1 - arg ,
2019-04-01 02:48:19 +03:00
BAD_ARRAY_SUFFIX ) ;
2024-03-04 06:40:26 +03:00
return ERR_PTR ( - EINVAL ) ;
2019-04-01 02:48:19 +03:00
}
* t3 = ' \0 ' ;
if ( kstrtouint ( t2 , 0 , & parg - > count ) | | ! parg - > count ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset + t2 - arg ,
2019-04-01 02:48:19 +03:00
BAD_ARRAY_NUM ) ;
2024-03-04 06:40:26 +03:00
return ERR_PTR ( - EINVAL ) ;
2019-04-01 02:48:19 +03:00
}
if ( parg - > count > MAX_ARRAY_LEN ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset + t2 - arg ,
2019-04-01 02:48:19 +03:00
ARRAY_TOO_BIG ) ;
2024-03-04 06:40:26 +03:00
return ERR_PTR ( - EINVAL ) ;
2019-04-01 02:48:19 +03:00
}
2018-04-25 15:21:55 +03:00
}
2012-04-09 13:11:44 +04:00
}
2024-03-04 06:40:26 +03:00
offs = t ? t - arg : 0 ;
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 ,
2022-08-20 16:43:18 +03:00
* we can find those by strcmp . But ignore for eprobes .
2019-06-19 18:08:37 +03:00
*/
2023-06-06 15:39:56 +03:00
if ( ! ( ctx - > flags & TPARG_FL_TEVENT ) & &
2022-08-20 16:43:21 +03:00
( strcmp ( arg , " $comm " ) = = 0 | | strcmp ( arg , " $COMM " ) = = 0 | |
strncmp ( arg , " \\ \" " , 2 ) = = 0 ) ) {
2024-01-23 18:02:34 +03:00
/* The type of $comm must be "string", and not an array type. */
if ( parg - > count | | ( t & & strcmp ( t , " string " ) ) ) {
2024-03-04 06:40:26 +03:00
trace_probe_log_err ( ctx - > offset + offs , NEED_STRING_TYPE ) ;
return ERR_PTR ( - EINVAL ) ;
2024-01-23 18:02:34 +03:00
}
2023-06-06 15:39:56 +03:00
parg - > type = find_fetch_type ( " string " , ctx - > flags ) ;
2019-05-07 16:56:02 +03:00
} else
2023-06-06 15:39:56 +03:00
parg - > type = find_fetch_type ( t , ctx - > flags ) ;
2024-03-04 06:40:26 +03:00
2012-04-09 13:11:44 +04:00
if ( ! parg - > type ) {
2024-03-04 06:40:26 +03:00
trace_probe_log_err ( ctx - > offset + offs , BAD_TYPE ) ;
return ERR_PTR ( - EINVAL ) ;
2012-04-09 13:11:44 +04:00
}
2024-03-04 06:40:26 +03:00
return t ;
}
2024-01-23 18:03:02 +03:00
2024-03-04 06:40:26 +03:00
/* After parsing, adjust the fetch_insn according to the probe_arg */
static int finalize_fetch_insn ( struct fetch_insn * code ,
struct probe_arg * parg ,
char * type ,
int type_offset ,
struct traceprobe_parse_context * ctx )
{
struct fetch_insn * scode ;
int ret ;
2023-06-06 15:39:56 +03:00
2018-04-25 15:18:03 +03:00
/* Store operation */
2022-11-14 07:47:56 +03:00
if ( parg - > type - > is_string ) {
2024-03-04 06:40:26 +03:00
/* Check bad combination of the type and the last fetch_insn. */
2022-11-14 07:47:56 +03:00
if ( ! strcmp ( parg - > type - > name , " symstr " ) ) {
if ( code - > op ! = FETCH_OP_REG & & code - > op ! = FETCH_OP_STACK & &
code - > op ! = FETCH_OP_RETVAL & & code - > op ! = FETCH_OP_ARG & &
code - > op ! = FETCH_OP_DEREF & & code - > op ! = FETCH_OP_TP_ARG ) {
2024-03-04 06:40:26 +03:00
trace_probe_log_err ( ctx - > offset + type_offset ,
2022-11-14 07:47:56 +03:00
BAD_SYMSTRING ) ;
2024-03-04 06:40:26 +03:00
return - EINVAL ;
2022-11-14 07:47:56 +03:00
}
} else {
if ( code - > op ! = FETCH_OP_DEREF & & code - > op ! = FETCH_OP_UDEREF & &
code - > op ! = FETCH_OP_IMM & & code - > op ! = FETCH_OP_COMM & &
code - > op ! = FETCH_OP_DATA & & code - > op ! = FETCH_OP_TP_ARG ) {
2024-03-04 06:40:26 +03:00
trace_probe_log_err ( ctx - > offset + type_offset ,
2022-11-14 07:47:56 +03:00
BAD_STRING ) ;
2024-03-04 06:40:26 +03:00
return - EINVAL ;
2022-11-14 07:47:56 +03:00
}
2018-04-25 15:18:03 +03:00
}
2024-03-04 06:40:26 +03:00
2022-11-14 07:47:56 +03:00
if ( ! strcmp ( parg - > type - > name , " symstr " ) | |
( code - > op = = FETCH_OP_IMM | | code - > op = = FETCH_OP_COMM | |
tracing: Add a probe that attaches to trace events
A new dynamic event is introduced: event probe. The event is attached
to an existing tracepoint and uses its fields as arguments. The user
can specify custom format string of the new event, select what tracepoint
arguments will be printed and how to print them.
An event probe is created by writing configuration string in
'dynamic_events' ftrace file:
e[:[SNAME/]ENAME] SYSTEM/EVENT [FETCHARGS] - Set an event probe
-:SNAME/ENAME - Delete an event probe
Where:
SNAME - System name, if omitted 'eprobes' is used.
ENAME - Name of the new event in SNAME, if omitted the SYSTEM_EVENT is used.
SYSTEM - Name of the system, where the tracepoint is defined, mandatory.
EVENT - Name of the tracepoint event in SYSTEM, mandatory.
FETCHARGS - Arguments:
<name>=$<field>[:TYPE] - Fetch given filed of the tracepoint and print
it as given TYPE with given name. Supported
types are:
(u8/u16/u32/u64/s8/s16/s32/s64), basic type
(x8/x16/x32/x64), hexadecimal types
"string", "ustring" and bitfield.
Example, attach an event probe on openat system call and print name of the
file that will be opened:
echo "e:esys/eopen syscalls/sys_enter_openat file=\$filename:string" >> dynamic_events
A new dynamic event is created in events/esys/eopen/ directory. It
can be deleted with:
echo "-:esys/eopen" >> dynamic_events
Filters, triggers and histograms can be attached to the new event, it can
be matched in synthetic events. There is one limitation - an event probe
can not be attached to kprobe, uprobe or another event probe.
Link: https://lkml.kernel.org/r/20210812145805.2292326-1-tz.stoyanov@gmail.com
Link: https://lkml.kernel.org/r/20210819152825.142428383@goodmis.org
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Co-developed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
2021-08-19 18:26:06 +03:00
code - > op = = FETCH_OP_DATA ) | | code - > op = = FETCH_OP_TP_ARG | |
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 .
2022-11-14 07:47:56 +03:00
* For the symstr , it doesn ' t need to dereference , thus
* it just get the value .
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 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , TOO_MANY_OPS ) ;
2024-03-04 06:40:26 +03:00
return - EINVAL ;
2018-04-25 15:21:55 +03:00
}
}
2024-03-04 06:40:26 +03:00
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 ;
2022-11-14 07:47:56 +03:00
else if ( ! strcmp ( parg - > type - > name , " symstr " ) )
code - > op = FETCH_OP_ST_SYMSTR ;
2019-05-15 08:38:30 +03:00
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 ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , TOO_MANY_OPS ) ;
2024-03-04 06:40:26 +03:00
return - E2BIG ;
2018-04-25 15:18:03 +03:00
}
code - > op = FETCH_OP_ST_RAW ;
code - > size = parg - > type - > size ;
}
2024-03-04 06:40:26 +03:00
/* Save storing fetch_insn. */
2018-04-25 15:21:55 +03:00
scode = code ;
2024-03-04 06:40:26 +03:00
2018-04-25 15:18:03 +03:00
/* Modify operation */
2024-03-04 06:40:26 +03:00
if ( type ! = NULL ) {
/* Bitfield needs a special fetch_insn. */
ret = __parse_bitfield_probe_arg ( type , parg - > type , & code ) ;
2019-04-01 02:48:19 +03:00
if ( ret ) {
2024-03-04 06:40:26 +03:00
trace_probe_log_err ( ctx - > offset + type_offset , BAD_BITFIELD ) ;
return ret ;
2019-04-01 02:48:19 +03:00
}
2023-08-22 19:26:12 +03:00
} else if ( IS_ENABLED ( CONFIG_PROBE_EVENTS_BTF_ARGS ) & &
ctx - > last_type ) {
2024-03-04 06:40:26 +03:00
/* If user not specified the type, try parsing BTF bitfield. */
2023-08-22 19:26:12 +03:00
ret = parse_btf_bitfield ( & code , ctx ) ;
if ( ret )
2024-03-04 06:40:26 +03:00
return ret ;
2012-04-09 13:11:44 +04:00
}
2024-03-04 06:40:26 +03:00
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 ) {
2024-03-04 06:40:26 +03:00
trace_probe_log_err ( ctx - > offset + type_offset , BAD_STRING ) ;
return - EINVAL ;
2018-04-25 15:21:55 +03:00
}
code + + ;
if ( code - > op ! = FETCH_OP_NOP ) {
2023-06-06 15:39:56 +03:00
trace_probe_log_err ( ctx - > offset , TOO_MANY_OPS ) ;
2024-03-04 06:40:26 +03:00
return - E2BIG ;
2018-04-25 15:21:55 +03:00
}
code - > op = FETCH_OP_LP_ARRAY ;
code - > param = parg - > count ;
}
2024-03-04 06:40:26 +03:00
/* Finalize the fetch_insn array. */
2018-04-25 15:18:03 +03:00
code + + ;
code - > op = FETCH_OP_END ;
2024-03-04 06:40:26 +03:00
return 0 ;
}
/* String length checking wrapper */
static int traceprobe_parse_probe_arg_body ( const char * argv , ssize_t * size ,
struct probe_arg * parg ,
struct traceprobe_parse_context * ctx )
{
struct fetch_insn * code , * tmp = NULL ;
char * type , * arg ;
int ret , len ;
len = strlen ( argv ) ;
if ( len > MAX_ARGSTR_LEN ) {
trace_probe_log_err ( ctx - > offset , ARG_TOO_LONG ) ;
return - E2BIG ;
} else if ( len = = 0 ) {
trace_probe_log_err ( ctx - > offset , NO_ARG_BODY ) ;
return - EINVAL ;
}
arg = kstrdup ( argv , GFP_KERNEL ) ;
if ( ! arg )
return - ENOMEM ;
parg - > comm = kstrdup ( arg , GFP_KERNEL ) ;
if ( ! parg - > comm ) {
ret = - ENOMEM ;
goto out ;
}
type = parse_probe_arg_type ( arg , parg , ctx ) ;
if ( IS_ERR ( type ) ) {
ret = PTR_ERR ( type ) ;
goto out ;
}
code = tmp = kcalloc ( FETCH_INSN_MAX , sizeof ( * code ) , GFP_KERNEL ) ;
if ( ! code ) {
ret = - ENOMEM ;
goto out ;
}
code [ FETCH_INSN_MAX - 1 ] . op = FETCH_OP_END ;
ctx - > last_type = NULL ;
ret = parse_probe_arg ( arg , parg - > type , & code , & code [ FETCH_INSN_MAX - 1 ] ,
ctx ) ;
if ( ret < 0 )
goto fail ;
/* Update storing type if BTF is available */
if ( IS_ENABLED ( CONFIG_PROBE_EVENTS_BTF_ARGS ) & &
ctx - > last_type ) {
if ( ! type ) {
parg - > type = find_fetch_type_from_btf_type ( ctx ) ;
} else if ( strstr ( type , " string " ) ) {
ret = check_prepare_btf_string_fetch ( type , & code , ctx ) ;
if ( ret )
goto fail ;
}
}
parg - > offset = * size ;
* 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 ) {
ret = - ENOMEM ;
goto out ;
}
snprintf ( parg - > fmt , len , " %s[%d] " , parg - > type - > fmttype ,
parg - > count ) ;
}
ret = finalize_fetch_insn ( code , parg , type , type ? type - arg : 0 , ctx ) ;
if ( ret < 0 )
goto fail ;
for ( ; code < tmp + FETCH_INSN_MAX ; code + + )
if ( code - > op = = FETCH_OP_END )
break ;
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 :
2024-03-04 06:40:26 +03:00
if ( ret < 0 ) {
2018-08-28 19:18:43 +03:00
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 ;
}
2023-06-06 15:39:56 +03:00
static char * generate_probe_arg_name ( const char * arg , int idx )
{
char * name = NULL ;
const char * end ;
/*
* If argument name is omitted , try arg as a name ( BTF variable )
* or " argN " .
*/
if ( IS_ENABLED ( CONFIG_PROBE_EVENTS_BTF_ARGS ) ) {
end = strchr ( arg , ' : ' ) ;
if ( ! end )
end = arg + strlen ( arg ) ;
name = kmemdup_nul ( arg , end - arg , GFP_KERNEL ) ;
if ( ! name | | ! is_good_name ( name ) ) {
kfree ( name ) ;
name = NULL ;
}
}
if ( ! name )
name = kasprintf ( GFP_KERNEL , " arg%d " , idx + 1 ) ;
return name ;
}
2021-08-17 06:42:58 +03:00
int traceprobe_parse_probe_arg ( struct trace_probe * tp , int i , const char * arg ,
2023-06-06 15:39:56 +03:00
struct traceprobe_parse_context * ctx )
2018-11-05 12:01:40 +03:00
{
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 {
2023-06-06 15:39:56 +03:00
parg - > name = generate_probe_arg_name ( arg , i ) ;
2018-11-05 12:01:40 +03:00
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 ;
}
2023-06-06 15:39:56 +03:00
ctx - > offset = body - arg ;
2018-11-05 12:01:40 +03:00
/* Parse fetch argument */
2023-06-06 15:39:56 +03:00
return traceprobe_parse_probe_arg_body ( body , & tp - > size , parg , ctx ) ;
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
}
2023-06-06 15:39:56 +03:00
static int argv_has_var_arg ( int argc , const char * argv [ ] , int * args_idx ,
struct traceprobe_parse_context * ctx )
{
int i , found = 0 ;
for ( i = 0 ; i < argc ; i + + )
if ( str_has_prefix ( argv [ i ] , " $arg " ) ) {
trace_probe_log_set_index ( i + 2 ) ;
if ( ! tparg_is_function_entry ( ctx - > flags ) ) {
trace_probe_log_err ( 0 , NOFENTRY_ARGS ) ;
return - EINVAL ;
}
if ( isdigit ( argv [ i ] [ 4 ] ) ) {
found = 1 ;
continue ;
}
if ( argv [ i ] [ 4 ] ! = ' * ' ) {
trace_probe_log_err ( 0 , BAD_VAR ) ;
return - EINVAL ;
}
if ( * args_idx > = 0 & & * args_idx < argc ) {
trace_probe_log_err ( 0 , DOUBLE_ARGS ) ;
return - EINVAL ;
}
found = 1 ;
* args_idx = i ;
}
return found ;
}
static int sprint_nth_btf_arg ( int idx , const char * type ,
char * buf , int bufsize ,
struct traceprobe_parse_context * ctx )
{
const char * name ;
int ret ;
if ( idx > = ctx - > nr_params ) {
trace_probe_log_err ( 0 , NO_BTFARG ) ;
return - ENOENT ;
}
2023-08-22 19:25:42 +03:00
name = btf_name_by_offset ( ctx - > btf , ctx - > params [ idx ] . name_off ) ;
2023-06-06 15:39:56 +03:00
if ( ! name ) {
trace_probe_log_err ( 0 , NO_BTF_ENTRY ) ;
return - ENOENT ;
}
ret = snprintf ( buf , bufsize , " %s%s " , name , type ) ;
if ( ret > = bufsize ) {
trace_probe_log_err ( 0 , ARGS_2LONG ) ;
return - E2BIG ;
}
return ret ;
}
/* Return new_argv which must be freed after use */
const char * * traceprobe_expand_meta_args ( int argc , const char * argv [ ] ,
int * new_argc , char * buf , int bufsize ,
struct traceprobe_parse_context * ctx )
{
const struct btf_param * params = NULL ;
int i , j , n , used , ret , args_idx = - 1 ;
const char * * new_argv = NULL ;
ret = argv_has_var_arg ( argc , argv , & args_idx , ctx ) ;
if ( ret < 0 )
return ERR_PTR ( ret ) ;
if ( ! ret ) {
* new_argc = argc ;
return NULL ;
}
2023-08-22 19:26:22 +03:00
ret = query_btf_context ( ctx ) ;
if ( ret < 0 | | ctx - > nr_params = = 0 ) {
2023-06-06 15:39:56 +03:00
if ( args_idx ! = - 1 ) {
/* $arg* requires BTF info */
trace_probe_log_err ( 0 , NOSUP_BTFARG ) ;
return ( const char * * ) params ;
}
2023-06-04 05:29:00 +03:00
* new_argc = argc ;
return NULL ;
2023-06-06 15:39:56 +03:00
}
if ( args_idx > = 0 )
* new_argc = argc + ctx - > nr_params - 1 ;
else
* new_argc = argc ;
new_argv = kcalloc ( * new_argc , sizeof ( char * ) , GFP_KERNEL ) ;
if ( ! new_argv )
return ERR_PTR ( - ENOMEM ) ;
used = 0 ;
for ( i = 0 , j = 0 ; i < argc ; i + + ) {
trace_probe_log_set_index ( i + 2 ) ;
if ( i = = args_idx ) {
2023-08-22 19:26:22 +03:00
for ( n = 0 ; n < ctx - > nr_params ; n + + ) {
2023-06-06 15:39:56 +03:00
ret = sprint_nth_btf_arg ( n , " " , buf + used ,
bufsize - used , ctx ) ;
if ( ret < 0 )
goto error ;
new_argv [ j + + ] = buf + used ;
used + = ret + 1 ;
}
continue ;
}
if ( str_has_prefix ( argv [ i ] , " $arg " ) ) {
char * type = NULL ;
n = simple_strtoul ( argv [ i ] + 4 , & type , 10 ) ;
if ( type & & ! ( * type = = ' : ' | | * type = = ' \0 ' ) ) {
trace_probe_log_err ( 0 , BAD_VAR ) ;
ret = - ENOENT ;
goto error ;
}
/* Note: $argN starts from $arg1 */
ret = sprint_nth_btf_arg ( n - 1 , type , buf + used ,
bufsize - used , ctx ) ;
if ( ret < 0 )
goto error ;
new_argv [ j + + ] = buf + used ;
used + = ret + 1 ;
} else
new_argv [ j + + ] = argv [ i ] ;
}
return new_argv ;
error :
kfree ( new_argv ) ;
return ERR_PTR ( ret ) ;
}
2023-08-22 19:25:42 +03:00
void traceprobe_finish_parse ( struct traceprobe_parse_context * ctx )
{
clear_btf_context ( ctx ) ;
}
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) " ;
2022-02-19 03:00:57 +03:00
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) " ;
2022-02-19 03:00:57 +03:00
arg = " , REC-> " FIELD_STRING_FUNC " , REC-> " FIELD_STRING_RETIP ;
2021-08-19 07:13:27 +03:00
break ;
tracing: Add a probe that attaches to trace events
A new dynamic event is introduced: event probe. The event is attached
to an existing tracepoint and uses its fields as arguments. The user
can specify custom format string of the new event, select what tracepoint
arguments will be printed and how to print them.
An event probe is created by writing configuration string in
'dynamic_events' ftrace file:
e[:[SNAME/]ENAME] SYSTEM/EVENT [FETCHARGS] - Set an event probe
-:SNAME/ENAME - Delete an event probe
Where:
SNAME - System name, if omitted 'eprobes' is used.
ENAME - Name of the new event in SNAME, if omitted the SYSTEM_EVENT is used.
SYSTEM - Name of the system, where the tracepoint is defined, mandatory.
EVENT - Name of the tracepoint event in SYSTEM, mandatory.
FETCHARGS - Arguments:
<name>=$<field>[:TYPE] - Fetch given filed of the tracepoint and print
it as given TYPE with given name. Supported
types are:
(u8/u16/u32/u64/s8/s16/s32/s64), basic type
(x8/x16/x32/x64), hexadecimal types
"string", "ustring" and bitfield.
Example, attach an event probe on openat system call and print name of the
file that will be opened:
echo "e:esys/eopen syscalls/sys_enter_openat file=\$filename:string" >> dynamic_events
A new dynamic event is created in events/esys/eopen/ directory. It
can be deleted with:
echo "-:esys/eopen" >> dynamic_events
Filters, triggers and histograms can be attached to the new event, it can
be matched in synthetic events. There is one limitation - an event probe
can not be attached to kprobe, uprobe or another event probe.
Link: https://lkml.kernel.org/r/20210812145805.2292326-1-tz.stoyanov@gmail.com
Link: https://lkml.kernel.org/r/20210819152825.142428383@goodmis.org
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Co-developed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
2021-08-19 18:26:06 +03:00
case PROBE_PRINT_EVENT :
2022-02-19 03:00:57 +03:00
fmt = " " ;
arg = " " ;
tracing: Add a probe that attaches to trace events
A new dynamic event is introduced: event probe. The event is attached
to an existing tracepoint and uses its fields as arguments. The user
can specify custom format string of the new event, select what tracepoint
arguments will be printed and how to print them.
An event probe is created by writing configuration string in
'dynamic_events' ftrace file:
e[:[SNAME/]ENAME] SYSTEM/EVENT [FETCHARGS] - Set an event probe
-:SNAME/ENAME - Delete an event probe
Where:
SNAME - System name, if omitted 'eprobes' is used.
ENAME - Name of the new event in SNAME, if omitted the SYSTEM_EVENT is used.
SYSTEM - Name of the system, where the tracepoint is defined, mandatory.
EVENT - Name of the tracepoint event in SYSTEM, mandatory.
FETCHARGS - Arguments:
<name>=$<field>[:TYPE] - Fetch given filed of the tracepoint and print
it as given TYPE with given name. Supported
types are:
(u8/u16/u32/u64/s8/s16/s32/s64), basic type
(x8/x16/x32/x64), hexadecimal types
"string", "ustring" and bitfield.
Example, attach an event probe on openat system call and print name of the
file that will be opened:
echo "e:esys/eopen syscalls/sys_enter_openat file=\$filename:string" >> dynamic_events
A new dynamic event is created in events/esys/eopen/ directory. It
can be deleted with:
echo "-:esys/eopen" >> dynamic_events
Filters, triggers and histograms can be attached to the new event, it can
be matched in synthetic events. There is one limitation - an event probe
can not be attached to kprobe, uprobe or another event probe.
Link: https://lkml.kernel.org/r/20210812145805.2292326-1-tz.stoyanov@gmail.com
Link: https://lkml.kernel.org/r/20210819152825.142428383@goodmis.org
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Co-developed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
2021-08-19 18:26:06 +03:00
break ;
2021-08-19 07:13:27 +03:00
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
}
2022-02-19 03:00:57 +03:00
pos + = snprintf ( buf + pos , LEN_OR_ZERO , " \" %s " , arg ) ;
2013-07-03 11:09:02 +04:00
for ( i = 0 ; i < tp - > nr_args ; i + + ) {
2018-04-25 15:21:55 +03:00
parg = tp - > args + i ;
if ( parg - > count ) {
2022-11-14 07:47:56 +03:00
if ( parg - > type - > is_string )
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 {
2022-11-14 07:47:56 +03:00
if ( parg - > type - > is_string )
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 ) ;
2023-02-01 18:08:10 +03:00
kvfree_rcu_mightsleep ( link ) ;
2019-05-31 18:17:26 +03:00
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 ;
}
2022-12-30 09:33:19 +03:00
int trace_probe_print_args ( struct trace_seq * s , struct probe_arg * args , int nr_args ,
u8 * data , void * field )
{
void * p ;
int i , j ;
for ( i = 0 ; i < nr_args ; i + + ) {
struct probe_arg * a = args + i ;
trace_seq_printf ( s , " %s= " , a - > name ) ;
if ( likely ( ! a - > count ) ) {
if ( ! a - > type - > print ( s , data + a - > offset , field ) )
return - ENOMEM ;
continue ;
}
trace_seq_putc ( s , ' { ' ) ;
p = data + a - > offset ;
for ( j = 0 ; j < a - > count ; j + + ) {
if ( ! a - > type - > print ( s , p , field ) )
return - ENOMEM ;
trace_seq_putc ( s , j = = a - > count - 1 ? ' } ' : ' , ' ) ;
p + = a - > type - > size ;
}
}
return 0 ;
}