All of lore.kernel.org
 help / color / mirror / Atom feed
From: Masami Hiramatsu <mhiramat@kernel.org>
To: Steven Rostedt <rostedt@goodmis.org>
Cc: Jiri Olsa <jolsa@redhat.com>,
	Alan Maguire <alan.maguire@oracle.com>,
	Masami Hiramatsu <mhiramat@kernel.org>,
	Sven Schnelle <svens@linux.ibm.com>,
	bpf@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [RFC PATCH] tracing/kprobe: Support $$args for function entry
Date: Thu, 23 Sep 2021 21:39:52 +0900	[thread overview]
Message-ID: <163240079198.34105.7585817231870405021.stgit@devnote2> (raw)
In-Reply-To: <163240078318.34105.12819521680435948398.stgit@devnote2>

Support $$args fetch arg for function entry. This uses
BTF for finding the function argument. Thus it depends
on CONFIG_BPF_SYSCALL.

/sys/kernel/tracing # echo 'p vfs_read $$args' >> kprobe_events
/sys/kernel/tracing # cat kprobe_events
p:kprobes/p_vfs_read_0 vfs_read file=$arg1:x64 buf=$arg2:x64 count=$arg3:u64 pos=$arg4:x64

Note that $$args must be used without argument name.

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
---
 kernel/trace/trace_kprobe.c |   60 ++++++++++++++++++++++++-
 kernel/trace/trace_probe.c  |  105 +++++++++++++++++++++++++++++++++++++++++++
 kernel/trace/trace_probe.h  |    5 ++
 3 files changed, 168 insertions(+), 2 deletions(-)

diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 3dd4fb719aa3..fe88ee8c8cd8 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -712,6 +712,58 @@ static int trace_kprobe_module_callback(struct notifier_block *nb,
 	return NOTIFY_DONE;
 }
 
+#ifdef CONFIG_BPF_SYSCALL
+
+static int trace_kprobe_parse_btf_args(struct trace_kprobe *tk, int i,
+				       const char *arg, unsigned int flags)
+{
+	struct trace_probe *tp = &tk->tp;
+	static struct btf *btf;
+	const struct btf_type *t;
+	const struct btf_param *args;
+	s32 id, nargs;
+	int ret;
+
+	if (!(flags & TPARG_FL_FENTRY))
+		return -EINVAL;
+	if (!tk->symbol)
+		return -EINVAL;
+
+	if (!btf)
+		btf = btf_parse_vmlinux();
+
+	id = btf_find_by_name_kind(btf, tk->symbol, BTF_KIND_FUNC);
+	if (id <= 0)
+		return -ENOENT;
+
+	/* Get BTF_KIND_FUNC type */
+	t = btf_type_by_id(btf, id);
+	if (!btf_type_is_func(t))
+		return -ENOENT;
+
+	/* The type of BTF_KIND_FUNC is BTF_KIND_FUNC_PROTO */
+	t = btf_type_by_id(btf, t->type);
+	if (!btf_type_is_func_proto(t))
+		return -ENOENT;
+
+	args = (const struct btf_param *)(t + 1);
+	nargs = btf_type_vlen(t);
+	for (i = 0; i < nargs; i++) {
+		ret = traceprobe_parse_btf_arg(tp, i, btf, &args[i]);
+		if (ret < 0)
+			break;
+	}
+
+	return ret;
+}
+#else
+static int trace_kprobe_parse_btf_args(struct trace_kprobe *tk, int i,
+				       const char *arg, unsigned int flags)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
 static struct notifier_block trace_kprobe_module_nb = {
 	.notifier_call = trace_kprobe_module_callback,
 	.priority = 1	/* Invoked after kprobe module callback */
@@ -733,12 +785,13 @@ static int __trace_kprobe_create(int argc, const char *argv[])
 	 *  $stack	: fetch stack address
 	 *  $stackN	: fetch Nth of stack (N:0-)
 	 *  $comm       : fetch current task comm
+	 *  $$args	: fetch parameters using BTF
 	 *  @ADDR	: fetch memory at ADDR (ADDR should be in kernel)
 	 *  @SYM[+|-offs] : fetch memory at SYM +|- offs (SYM is a data symbol)
 	 *  %REG	: fetch register REG
 	 * Dereferencing memory fetch:
 	 *  +|-offs(ARG) : fetch memory at ARG +|- offs address.
-	 * Alias name of args:
+	 * Alias name of args (except for $$args) :
 	 *  NAME=FETCHARG : set NAME as alias of FETCHARG.
 	 * Type of args:
 	 *  FETCHARG:TYPE : use TYPE instead of unsigned long.
@@ -877,7 +930,10 @@ static int __trace_kprobe_create(int argc, const char *argv[])
 	/* parse arguments */
 	for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
 		trace_probe_log_set_index(i + 2);
-		ret = traceprobe_parse_probe_arg(&tk->tp, i, argv[i], flags);
+		if (strcmp(argv[i], "$$args") == 0)
+			ret = trace_kprobe_parse_btf_args(tk, i, argv[i], flags);
+		else
+			ret = traceprobe_parse_probe_arg(&tk->tp, i, argv[i], flags);
 		if (ret)
 			goto error;	/* This can be -ENOMEM */
 	}
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 2fe104109525..bbac261b1688 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -765,6 +765,111 @@ static int traceprobe_conflict_field_name(const char *name,
 	return 0;
 }
 
+#ifdef CONFIG_BPF_SYSCALL
+
+static u32 btf_type_int(const struct btf_type *t)
+{
+	return *(u32 *)(t + 1);
+}
+
+static const char *traceprobe_type_from_btf(struct btf *btf, s32 id)
+{
+	const struct btf_type *t;
+	u32 intdata;
+	s32 tid;
+
+	/* TODO: const char * could be converted as a string */
+	t = btf_type_skip_modifiers(btf, id, &tid);
+
+	switch (BTF_INFO_KIND(t->info)) {
+	case BTF_KIND_ENUM:
+		/* enum is "int", so convert to "s32" */
+		return "s32";
+	case BTF_KIND_PTR:
+		/* pointer will be converted to "x??" */
+		if (IS_ENABLED(CONFIG_64BIT))
+			return "x64";
+		else
+			return "x32";
+	case BTF_KIND_INT:
+		intdata = btf_type_int(t);
+		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";
+			}
+		}
+	}
+
+	/* Default type */
+	if (IS_ENABLED(CONFIG_64BIT))
+		return "x64";
+	else
+		return "x32";
+}
+
+int traceprobe_parse_btf_arg(struct trace_probe *tp, int i, struct btf *btf,
+			     const struct btf_param *arg)
+{
+	struct probe_arg *parg = &tp->args[i];
+	const char *name, *tname;
+	char *body;
+	int ret;
+
+	tp->nr_args++;
+	name = btf_name_by_offset(btf, arg->name_off);
+	parg->name = kstrdup(name, GFP_KERNEL);
+	if (!parg->name)
+		return -ENOMEM;
+
+	if (!is_good_name(parg->name)) {
+		trace_probe_log_err(0, BAD_ARG_NAME);
+		return -EINVAL;
+	}
+	if (traceprobe_conflict_field_name(parg->name, tp->args, i)) {
+		trace_probe_log_err(0, USED_ARG_NAME);
+		return -EINVAL;
+	}
+
+	/*
+	 * Since probe event needs an appropriate command for dyn_event interface,
+	 * convert BTF type to corresponding fetch-type string.
+	 */
+	tname = traceprobe_type_from_btf(btf, arg->type);
+	if (tname)
+		body = kasprintf(GFP_KERNEL, "$arg%d:%s", i + 1, tname);
+	else
+		body = kasprintf(GFP_KERNEL, "$arg%d", i + 1);
+
+	if (!body)
+		return -ENOMEM;
+	/* Parse fetch argument */
+	ret = traceprobe_parse_probe_arg_body(body, &tp->size, parg,
+				TPARG_FL_KERNEL | TPARG_FL_FENTRY, 0);
+
+	kfree(body);
+
+	return ret;
+}
+#endif
+
 int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, const char *arg,
 				unsigned int flags)
 {
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 355c78a930f8..857b946afe29 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -23,6 +23,7 @@
 #include <linux/limits.h>
 #include <linux/uaccess.h>
 #include <linux/bitops.h>
+#include <linux/btf.h>
 #include <asm/bitsperlong.h>
 
 #include "trace.h"
@@ -359,6 +360,10 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char
 
 extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
 				const char *argv, unsigned int flags);
+#ifdef CONFIG_BPF_SYSCALL
+int traceprobe_parse_btf_arg(struct trace_probe *tp, int i, struct btf *btf,
+			     const struct btf_param *arg);
+#endif
 
 extern int traceprobe_update_arg(struct probe_arg *arg);
 extern void traceprobe_free_probe_arg(struct probe_arg *arg);


  reply	other threads:[~2021-09-23 12:41 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-23 12:39 [RFC PATCH] tracing: BTF testing for kprobe-events Masami Hiramatsu
2021-09-23 12:39 ` Masami Hiramatsu [this message]
2021-09-24 17:51   ` [RFC PATCH] tracing/kprobe: Support $$args for function entry Alan Maguire
2021-09-25 14:15     ` Masami Hiramatsu
2021-10-11 22:23 ` [RFC PATCH] tracing: BTF testing for kprobe-events Steven Rostedt
2021-10-13 12:52   ` Masami Hiramatsu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=163240079198.34105.7585817231870405021.stgit@devnote2 \
    --to=mhiramat@kernel.org \
    --cc=alan.maguire@oracle.com \
    --cc=bpf@vger.kernel.org \
    --cc=jolsa@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=svens@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.