]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
tracing: probeevent: Add $argN for accessing function args
authorMasami Hiramatsu <mhiramat@kernel.org>
Wed, 25 Apr 2018 12:21:26 +0000 (21:21 +0900)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Thu, 11 Oct 2018 02:19:11 +0000 (22:19 -0400)
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.

Link: http://lkml.kernel.org/r/152465888632.26224.3412465701570253696.stgit@devbox
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Documentation/trace/kprobetrace.rst
kernel/trace/trace.c
kernel/trace/trace_kprobe.c
kernel/trace/trace_probe.c
kernel/trace/trace_probe.h
kernel/trace/trace_uprobe.c

index 2dfed7a1ea6f7e934a537708b8f9d166d215865a..47e765c2f2c379851393084405aa8f45f52b5944 100644 (file)
@@ -45,16 +45,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
 -----
index e7f99f51395986a3e657e5d7c07d6a34aed774a5..ec5b21778806c91ae7da665e2910d777b04cf240 100644 (file)
@@ -4625,7 +4625,11 @@ static const char readme_msg[] =
 #endif
        "\t     args: <name>=fetcharg[:type]\n"
        "\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n"
+#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
+       "\t           $stack<index>, $stack, $retval, $comm, $arg<N>\n"
+#else
        "\t           $stack<index>, $stack, $retval, $comm\n"
+#endif
        "\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"
        "\t           <type>\\[<array-size>\\]\n"
index fdd43f2f1fd12e8338e606418ada2c913a950b5e..3faaadbddf54714375d283553543ac7e360a0c1c 100644 (file)
@@ -533,13 +533,15 @@ static int create_trace_kprobe(int argc, char **argv)
        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"
@@ -625,8 +627,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;
                }
@@ -696,7 +699,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;
@@ -932,6 +935,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;
        }
index dfd09603130514d85c6a136867f032eae5d5e432..333cda6d263364f3f5525d281e6ec294220d96f3 100644 (file)
@@ -157,14 +157,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;
@@ -173,7 +172,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;
@@ -183,6 +183,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;
 
@@ -193,7 +205,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;
@@ -203,8 +215,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 */
@@ -226,7 +237,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);
@@ -237,7 +248,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);
@@ -278,8 +289,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)
@@ -339,7 +349,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, *scode, *tmp = NULL;
        char *t, *t2;
@@ -397,7 +407,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;
 
index 1f456fd82483a3ca26560ecd847bea96fc940b8f..09f62171cc2396da695c8d09b695eafa0b0e507b 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/stringify.h>
 #include <linux/limits.h>
 #include <linux/uaccess.h>
+#include <linux/bitops.h>
 #include <asm/bitsperlong.h>
 
 #include "trace.h"
@@ -86,6 +87,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 */
@@ -263,8 +265,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);
index 7154473ffaa45b453ed3d7e73656b271661ba7c9..394b935725067a87a6e81b1e214872c051885556 100644 (file)
@@ -557,7 +557,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;