Re: [PATCH v2 13/17] tracing: probeevent: Add $argN for accessing function args

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 22 Feb 2018 00:00:31 +0900
Masami Hiramatsu <mhiramat@xxxxxxxxxx> wrote:

> Add $argN special fetch variable for accessing function
> arguments. This allows user to trace the Nth argument easily
> at the function entry.
> 
> Note that this returns most probably assignment of registers
> and stacks. In some case, it may not work well. If you need
> to access correct registers or stacks you should use perf-probe.
> 
> Signed-off-by: Masami Hiramatsu <mhiramat@xxxxxxxxxx>
> ---
>  Changes in v2:
>   - Add $argN in README file
>   - Make N start from 1 as same as auto-generate event argument
>     names.
> ---
>  Documentation/trace/kprobetrace.txt |   10 ++++++----
>  kernel/trace/trace.c                |    2 +-
>  kernel/trace/trace_kprobe.c         |   18 +++++++++++++-----
>  kernel/trace/trace_probe.c          |   36 ++++++++++++++++++++++-------------
>  kernel/trace/trace_probe.h          |    9 ++++++++-
>  kernel/trace/trace_uprobe.c         |    2 +-
>  6 files changed, 52 insertions(+), 25 deletions(-)
> 
> diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
> index d49381f2e411..1d082f8ffeee 100644
> --- a/Documentation/trace/kprobetrace.txt
> +++ b/Documentation/trace/kprobetrace.txt
> @@ -43,16 +43,18 @@ Synopsis of kprobe_events
>    @SYM[+|-offs]	: Fetch memory at SYM +|- offs (SYM should be a data symbol)
>    $stackN	: Fetch Nth entry of stack (N >= 0)
>    $stack	: Fetch stack address.
> -  $retval	: Fetch return value.(*)
> +  $argN		: Fetch the Nth function argument. (N >= 1) (*1)
> +  $retval	: Fetch return value.(*2)
>    $comm		: Fetch current task comm.
> -  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
> +  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(*3)
>    NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
>    FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
>  		  (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
>  		  (x8/x16/x32/x64), "string" and bitfield are supported.
>  
> -  (*) only for return probe.
> -  (**) this is useful for fetching a field of data structures.
> +  (*1) only for the probe on function entry (offs == 0).
> +  (*2) only for return probe.
> +  (*3) this is useful for fetching a field of data structures.
>  
>  Types
>  -----
> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index 8f08811d15b8..94423529b986 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -4608,7 +4608,7 @@ static const char readme_msg[] =
>  #endif
>  	"\t     args: <name>=fetcharg[:type]\n"
>  	"\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n"
> -	"\t           $stack<index>, $stack, $retval, $comm\n"
> +	"\t           $stack<index>, $stack, $retval, $comm, $arg<N>\n"

Oops, this should depend on CONFIG_HAVE_FUNCTION_ARG_ACCESS_API, or
testcase can not detect the kernel supports argN or not.

Thanks,

>  	"\t     type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
>  	"\t           b<bit-width>@<bit-offset>/<container-size>\n"
>  #endif
> diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
> index 5025907e0c99..df39c7d5dd4c 100644
> --- a/kernel/trace/trace_kprobe.c
> +++ b/kernel/trace/trace_kprobe.c
> @@ -490,13 +490,15 @@ static int create_trace_kprobe(int argc, char **argv)
>  	unsigned long offset = 0;
>  	void *addr = NULL;
>  	char buf[MAX_EVENT_NAME_LEN];
> +	unsigned int flags = TPARG_FL_KERNEL;
>  
>  	/* argc must be >= 1 */
>  	if (argv[0][0] == 'p')
>  		is_return = false;
> -	else if (argv[0][0] == 'r')
> +	else if (argv[0][0] == 'r') {
>  		is_return = true;
> -	else if (argv[0][0] == '-')
> +		flags |= TPARG_FL_RETURN;
> +	} else if (argv[0][0] == '-')
>  		is_delete = true;
>  	else {
>  		pr_info("Probe definition must be started with 'p', 'r' or"
> @@ -579,8 +581,9 @@ static int create_trace_kprobe(int argc, char **argv)
>  			pr_info("Failed to parse either an address or a symbol.\n");
>  			return ret;
>  		}
> -		if (offset && is_return &&
> -		    !kprobe_on_func_entry(NULL, symbol, offset)) {
> +		if (kprobe_on_func_entry(NULL, symbol, offset))
> +			flags |= TPARG_FL_FENTRY;
> +		if (offset && is_return && !(flags & TPARG_FL_FENTRY)) {
>  			pr_info("Given offset is not valid for return probe.\n");
>  			return -EINVAL;
>  		}
> @@ -650,7 +653,7 @@ static int create_trace_kprobe(int argc, char **argv)
>  
>  		/* Parse fetch argument */
>  		ret = traceprobe_parse_probe_arg(arg, &tk->tp.size, parg,
> -						 is_return, true);
> +						 flags);
>  		if (ret) {
>  			pr_info("Parse error at argument[%d]. (%d)\n", i, ret);
>  			goto error;
> @@ -890,6 +893,11 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
>  	case FETCH_OP_COMM:
>  		val = (unsigned long)current->comm;
>  		break;
> +#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
> +	case FETCH_OP_ARG:
> +		val = regs_get_kernel_argument(regs, code->param);
> +		break;
> +#endif
>  	default:
>  		return -EILSEQ;
>  	}
> diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
> index f41a2989130c..f4b83927d6c0 100644
> --- a/kernel/trace/trace_probe.c
> +++ b/kernel/trace/trace_probe.c
> @@ -171,14 +171,13 @@ int traceprobe_split_symbol_offset(char *symbol, long *offset)
>  #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
>  
>  static int parse_probe_vars(char *arg, const struct fetch_type *t,
> -			    struct fetch_insn *code, bool is_return,
> -			    bool is_kprobe)
> +			    struct fetch_insn *code, unsigned int flags)
>  {
>  	int ret = 0;
>  	unsigned long param;
>  
>  	if (strcmp(arg, "retval") == 0) {
> -		if (is_return)
> +		if (flags & TPARG_FL_RETURN)
>  			code->op = FETCH_OP_RETVAL;
>  		else
>  			ret = -EINVAL;
> @@ -187,7 +186,8 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
>  			code->op = FETCH_OP_STACKP;
>  		} else if (isdigit(arg[5])) {
>  			ret = kstrtoul(arg + 5, 10, &param);
> -			if (ret || (is_kprobe && param > PARAM_MAX_STACK))
> +			if (ret || ((flags & TPARG_FL_KERNEL) &&
> +				    param > PARAM_MAX_STACK))
>  				ret = -EINVAL;
>  			else {
>  				code->op = FETCH_OP_STACK;
> @@ -197,6 +197,18 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
>  			ret = -EINVAL;
>  	} else if (strcmp(arg, "comm") == 0) {
>  		code->op = FETCH_OP_COMM;
> +#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
> +	} else if (((flags & TPARG_FL_MASK) ==
> +		    (TPARG_FL_KERNEL | TPARG_FL_FENTRY)) &&
> +		   strncmp(arg, "arg", 3) == 0) {
> +		if (!isdigit(arg[3]))
> +			return -EINVAL;
> +		ret = kstrtoul(arg + 3, 10, &param);
> +		if (ret || !param || param > PARAM_MAX_STACK)
> +			return -EINVAL;
> +		code->op = FETCH_OP_ARG;
> +		code->param = (unsigned int)param - 1;
> +#endif
>  	} else
>  		ret = -EINVAL;
>  
> @@ -207,7 +219,7 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
>  static int
>  parse_probe_arg(char *arg, const struct fetch_type *type,
>  		struct fetch_insn **pcode, struct fetch_insn *end,
> -		bool is_return, bool is_kprobe)
> +		unsigned int flags)
>  {
>  	struct fetch_insn *code = *pcode;
>  	unsigned long param;
> @@ -217,8 +229,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
>  
>  	switch (arg[0]) {
>  	case '$':
> -		ret = parse_probe_vars(arg + 1, type, code,
> -					is_return, is_kprobe);
> +		ret = parse_probe_vars(arg + 1, type, code, flags);
>  		break;
>  
>  	case '%':	/* named register */
> @@ -240,7 +251,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
>  			code->immediate = param;
>  		} else if (arg[1] == '+') {
>  			/* kprobes don't support file offsets */
> -			if (is_kprobe)
> +			if (flags & TPARG_FL_KERNEL)
>  				return -EINVAL;
>  
>  			ret = kstrtol(arg + 2, 0, &offset);
> @@ -251,7 +262,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
>  			code->immediate = (unsigned long)offset;  // imm64?
>  		} else {
>  			/* uprobes don't support symbols */
> -			if (!is_kprobe)
> +			if (!(flags & TPARG_FL_KERNEL))
>  				return -EINVAL;
>  
>  			ret = traceprobe_split_symbol_offset(arg + 1, &offset);
> @@ -292,8 +303,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
>  			const struct fetch_type *t2 = find_fetch_type(NULL);
>  
>  			*tmp = '\0';
> -			ret = parse_probe_arg(arg, t2, &code, end, is_return,
> -					      is_kprobe);
> +			ret = parse_probe_arg(arg, t2, &code, end, flags);
>  			if (ret)
>  				break;
>  			if (code->op == FETCH_OP_COMM)
> @@ -353,7 +363,7 @@ static int __parse_bitfield_probe_arg(const char *bf,
>  
>  /* String length checking wrapper */
>  int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
> -		struct probe_arg *parg, bool is_return, bool is_kprobe)
> +		struct probe_arg *parg, unsigned int flags)
>  {
>  	struct fetch_insn *code, *tmp = NULL;
>  	const char *t;
> @@ -393,7 +403,7 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
>  	code[FETCH_INSN_MAX - 1].op = FETCH_OP_END;
>  
>  	ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1],
> -			      is_return, is_kprobe);
> +			      flags);
>  	if (ret)
>  		goto fail;
>  
> diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
> index ef477bd8468a..ff91faf70887 100644
> --- a/kernel/trace/trace_probe.h
> +++ b/kernel/trace/trace_probe.h
> @@ -35,6 +35,7 @@
>  #include <linux/stringify.h>
>  #include <linux/limits.h>
>  #include <linux/uaccess.h>
> +#include <linux/bitops.h>
>  #include <asm/bitsperlong.h>
>  
>  #include "trace.h"
> @@ -89,6 +90,7 @@ enum fetch_op {
>  	FETCH_OP_RETVAL,	/* Return value */
>  	FETCH_OP_IMM,		/* Immediate : .immediate */
>  	FETCH_OP_COMM,		/* Current comm */
> +	FETCH_OP_ARG,		/* Function argument : .param */
>  	FETCH_OP_FOFFS,		/* File offset: .immediate */
>  	// Stage 2 (dereference) op
>  	FETCH_OP_DEREF,		/* Dereference: .offset */
> @@ -261,8 +263,13 @@ find_event_file_link(struct trace_probe *tp, struct trace_event_file *file)
>  	return NULL;
>  }
>  
> +#define TPARG_FL_RETURN BIT(0)
> +#define TPARG_FL_KERNEL BIT(1)
> +#define TPARG_FL_FENTRY BIT(2)
> +#define TPARG_FL_MASK	GENMASK(2, 0)
> +
>  extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
> -		   struct probe_arg *parg, bool is_return, bool is_kprobe);
> +		   struct probe_arg *parg, unsigned int flags);
>  
>  extern int traceprobe_conflict_field_name(const char *name,
>  			       struct probe_arg *args, int narg);
> diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
> index 64c1fbe087a1..e15da2281855 100644
> --- a/kernel/trace/trace_uprobe.c
> +++ b/kernel/trace/trace_uprobe.c
> @@ -519,7 +519,7 @@ static int create_trace_uprobe(int argc, char **argv)
>  
>  		/* Parse fetch argument */
>  		ret = traceprobe_parse_probe_arg(arg, &tu->tp.size, parg,
> -						 is_return, false);
> +					is_return ? TPARG_FL_RETURN : 0);
>  		if (ret) {
>  			pr_info("Parse error at argument[%d]. (%d)\n", i, ret);
>  			goto error;
> 


-- 
Masami Hiramatsu <mhiramat@xxxxxxxxxx>
--
To unsubscribe from this list: send the line "unsubscribe linux-trace-users" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Development]     [Linux USB Development]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux