tracing/probes: support '%pd' type for print struct dentry's name
authorYe Bin <yebin10@huawei.com>
Fri, 22 Mar 2024 06:43:04 +0000 (14:43 +0800)
committerMasami Hiramatsu (Google) <mhiramat@kernel.org>
Wed, 1 May 2024 14:18:47 +0000 (23:18 +0900)
During fault locating, the file name needs to be printed based on the
dentry  address. The offset needs to be calculated each time, which
is troublesome. Similar to printk, kprobe support print type '%pd' for
print dentry's name. For example "name=$arg1:%pd" casts the `$arg1`
as (struct dentry *), dereferences the "d_name.name" field and stores
it to "name" argument as a kernel string.
Here is an example:
[tracing]# echo 'p:testprobe dput name=$arg1:%pd' > kprobe_events
[tracing]# echo 1 > events/kprobes/testprobe/enable
[tracing]# grep -q "1" events/kprobes/testprobe/enable
[tracing]# echo 0 > events/kprobes/testprobe/enable
[tracing]# cat trace | grep "enable"
    bash-14844   [002] ..... 16912.889543: testprobe: (dput+0x4/0x30) name="enable"
            grep-15389   [003] ..... 16922.834182: testprobe: (dput+0x4/0x30) name="enable"
            grep-15389   [003] ..... 16922.836103: testprobe: (dput+0x4/0x30) name="enable"
            bash-14844   [001] ..... 16931.820909: testprobe: (dput+0x4/0x30) name="enable"

Note that this expects the given argument (e.g. $arg1) is an address of struct
dentry. User must ensure it.

Link: https://lore.kernel.org/all/20240322064308.284457-2-yebin10@huawei.com/
Signed-off-by: Ye Bin <yebin10@huawei.com>
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
kernel/trace/trace.c
kernel/trace/trace_fprobe.c
kernel/trace/trace_kprobe.c
kernel/trace/trace_probe.c
kernel/trace/trace_probe.h

index 233d1af39fffe29b459c09e9a5e0a82fa07e992a..869978d8467291841a55569a34fc7be5273b791c 100644 (file)
@@ -5540,7 +5540,7 @@ static const char readme_msg[] =
        "\t     kernel return probes support: $retval, $arg<N>, $comm\n"
        "\t     type: s8/16/32/64, u8/16/32/64, x8/16/32/64, char, string, symbol,\n"
        "\t           b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
-       "\t           symstr, <type>\\[<array-size>\\]\n"
+       "\t           symstr, %pd, <type>\\[<array-size>\\]\n"
 #ifdef CONFIG_HIST_TRIGGERS
        "\t    field: <stype> <name>;\n"
        "\t    stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n"
index 4f428081552253c36124c1318cff6924ce307700..62e6a8f4aae9b7a04b337416f3566853d284d523 100644 (file)
@@ -994,6 +994,7 @@ static int __trace_fprobe_create(int argc, const char *argv[])
        char gbuf[MAX_EVENT_NAME_LEN];
        char sbuf[KSYM_NAME_LEN];
        char abuf[MAX_BTF_ARGS_LEN];
+       char *dbuf = NULL;
        bool is_tracepoint = false;
        struct tracepoint *tpoint = NULL;
        struct traceprobe_parse_context ctx = {
@@ -1104,6 +1105,10 @@ static int __trace_fprobe_create(int argc, const char *argv[])
                argv = new_argv;
        }
 
+       ret = traceprobe_expand_dentry_args(argc, argv, &dbuf);
+       if (ret)
+               goto out;
+
        /* setup a probe */
        tf = alloc_trace_fprobe(group, event, symbol, tpoint, maxactive,
                                argc, is_return);
@@ -1154,6 +1159,7 @@ out:
        trace_probe_log_clear();
        kfree(new_argv);
        kfree(symbol);
+       kfree(dbuf);
        return ret;
 
 parse_error:
index 14099cc17fc9ed5bfad51b5be14504ef790e50a6..c68d4e830fbe8561b1518af212ca015fca100538 100644 (file)
@@ -782,6 +782,7 @@ static int __trace_kprobe_create(int argc, const char *argv[])
        char buf[MAX_EVENT_NAME_LEN];
        char gbuf[MAX_EVENT_NAME_LEN];
        char abuf[MAX_BTF_ARGS_LEN];
+       char *dbuf = NULL;
        struct traceprobe_parse_context ctx = { .flags = TPARG_FL_KERNEL };
 
        switch (argv[0][0]) {
@@ -933,6 +934,10 @@ static int __trace_kprobe_create(int argc, const char *argv[])
                argv = new_argv;
        }
 
+       ret = traceprobe_expand_dentry_args(argc, argv, &dbuf);
+       if (ret)
+               goto out;
+
        /* setup a probe */
        tk = alloc_trace_kprobe(group, event, addr, symbol, offset, maxactive,
                                argc, is_return);
@@ -979,6 +984,7 @@ out:
        trace_probe_log_clear();
        kfree(new_argv);
        kfree(symbol);
+       kfree(dbuf);
        return ret;
 
 parse_error:
index dfe3ee6035ecc74da70ebd8104d23f1ef2a25cde..f7ee97e45d1f3aacde1ffae7aef1bbb2998fc2bc 100644 (file)
@@ -1739,6 +1739,56 @@ error:
        return ERR_PTR(ret);
 }
 
+/* @buf: *buf must be equal to NULL. Caller must to free *buf */
+int traceprobe_expand_dentry_args(int argc, const char *argv[], char **buf)
+{
+       int i, used, ret;
+       const int bufsize = MAX_DENTRY_ARGS_LEN;
+       char *tmpbuf = NULL;
+
+       if (*buf)
+               return -EINVAL;
+
+       used = 0;
+       for (i = 0; i < argc; i++) {
+               if (glob_match("*:%pd", argv[i])) {
+                       char *tmp;
+                       char *equal;
+
+                       if (!tmpbuf) {
+                               tmpbuf = kmalloc(bufsize, GFP_KERNEL);
+                               if (!tmpbuf)
+                                       return -ENOMEM;
+                       }
+
+                       tmp = kstrdup(argv[i], GFP_KERNEL);
+                       if (!tmp)
+                               goto nomem;
+
+                       equal = strchr(tmp, '=');
+                       if (equal)
+                               *equal = '\0';
+                       tmp[strlen(argv[i]) - 4] = '\0';
+                       ret = snprintf(tmpbuf + used, bufsize - used,
+                                      "%s%s+0x0(+0x%zx(%s)):string",
+                                      equal ? tmp : "", equal ? "=" : "",
+                                      offsetof(struct dentry, d_name.name),
+                                      equal ? equal + 1 : tmp);
+                       kfree(tmp);
+                       if (ret >= bufsize - used)
+                               goto nomem;
+                       argv[i] = tmpbuf + used;
+                       used += ret + 1;
+               }
+       }
+
+       *buf = tmpbuf;
+       return 0;
+nomem:
+       kfree(tmpbuf);
+       return -ENOMEM;
+}
+
 void traceprobe_finish_parse(struct traceprobe_parse_context *ctx)
 {
        clear_btf_context(ctx);
index cef3a50628a3e474dae7bf8b280ab430a04d607f..5803e6a415705505578ec1ca8d0264a49956b7b5 100644 (file)
@@ -34,6 +34,7 @@
 #define MAX_ARRAY_LEN          64
 #define MAX_ARG_NAME_LEN       32
 #define MAX_BTF_ARGS_LEN       128
+#define MAX_DENTRY_ARGS_LEN    256
 #define MAX_STRING_SIZE                PATH_MAX
 #define MAX_ARG_BUF_LEN                (MAX_TRACE_ARGS * MAX_ARG_NAME_LEN)
 
@@ -428,6 +429,7 @@ extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
 const char **traceprobe_expand_meta_args(int argc, const char *argv[],
                                         int *new_argc, char *buf, int bufsize,
                                         struct traceprobe_parse_context *ctx);
+extern int traceprobe_expand_dentry_args(int argc, const char *argv[], char **buf);
 
 extern int traceprobe_update_arg(struct probe_arg *arg);
 extern void traceprobe_free_probe_arg(struct probe_arg *arg);