]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
tracing: probeevent: Return consumed bytes of dynamic area
authorMasami Hiramatsu <mhiramat@kernel.org>
Wed, 25 Apr 2018 12:19:01 +0000 (21:19 +0900)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Thu, 11 Oct 2018 02:19:08 +0000 (22:19 -0400)
Cleanup string fetching routine so that returns the consumed
bytes of dynamic area and store the string information as
data_loc format instead of data_rloc.
This simplifies the fetcharg loop.

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

index dc1c638daf4455fd86c3f98fc0fe8827bf150a3a..7e5064f8ab8ff5e2a3f19f6219732d39dac57956 100644 (file)
@@ -853,8 +853,8 @@ static const struct file_operations kprobe_profile_ops = {
 /* Kprobe specific fetch functions */
 
 /* Return the length of string -- including null terminal byte */
-static nokprobe_inline void
-fetch_store_strlen(unsigned long addr, void *dest)
+static nokprobe_inline int
+fetch_store_strlen(unsigned long addr)
 {
        mm_segment_t old_fs;
        int ret, len = 0;
@@ -872,47 +872,40 @@ fetch_store_strlen(unsigned long addr, void *dest)
        pagefault_enable();
        set_fs(old_fs);
 
-       if (ret < 0)    /* Failed to check the length */
-               *(u32 *)dest = 0;
-       else
-               *(u32 *)dest = len;
+       return (ret < 0) ? ret : len;
 }
 
 /*
  * Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max
  * length and relative data location.
  */
-static nokprobe_inline void
-fetch_store_string(unsigned long addr, void *dest)
+static nokprobe_inline int
+fetch_store_string(unsigned long addr, void *dest, void *base)
 {
-       int maxlen = get_rloc_len(*(u32 *)dest);
-       u8 *dst = get_rloc_data(dest);
+       int maxlen = get_loc_len(*(u32 *)dest);
+       u8 *dst = get_loc_data(dest, base);
        long ret;
 
-       if (!maxlen)
-               return;
-
+       if (unlikely(!maxlen))
+               return -ENOMEM;
        /*
         * Try to get string again, since the string can be changed while
         * probing.
         */
        ret = strncpy_from_unsafe(dst, (void *)addr, maxlen);
 
-       if (ret < 0) {  /* Failed to fetch string */
-               dst[0] = '\0';
-               *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest));
-       } else {
-               *(u32 *)dest = make_data_rloc(ret, get_rloc_offs(*(u32 *)dest));
-       }
+       if (ret >= 0)
+               *(u32 *)dest = make_data_loc(ret, (void *)dst - base);
+       return ret;
 }
 
 /* Note that we don't verify it, since the code does not come from user space */
 static int
 process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
-                  bool pre)
+                  void *base)
 {
        unsigned long val;
-       int ret;
+       int ret = 0;
 
        /* 1st stage: get value from context */
        switch (code->op) {
@@ -949,6 +942,13 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
        }
 
        /* 3rd stage: store value to buffer */
+       if (unlikely(!dest)) {
+               if (code->op == FETCH_OP_ST_STRING)
+                       return fetch_store_strlen(val + code->offset);
+               else
+                       return -EILSEQ;
+       }
+
        switch (code->op) {
        case FETCH_OP_ST_RAW:
                fetch_store_raw(val, code, dest);
@@ -957,10 +957,7 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
                probe_kernel_read(dest, (void *)val + code->offset, code->size);
                break;
        case FETCH_OP_ST_STRING:
-               if (pre)
-                       fetch_store_strlen(val + code->offset, dest);
-               else
-                       fetch_store_string(val + code->offset, dest);
+               ret = fetch_store_string(val + code->offset, dest, base);
                break;
        default:
                return -EILSEQ;
@@ -973,7 +970,7 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
                code++;
        }
 
-       return code->op == FETCH_OP_END ? 0 : -EILSEQ;
+       return code->op == FETCH_OP_END ? ret : -EILSEQ;
 }
 NOKPROBE_SYMBOL(process_fetch_insn)
 
@@ -1008,7 +1005,7 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
 
        entry = ring_buffer_event_data(event);
        entry->ip = (unsigned long)tk->rp.kp.addr;
-       store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize);
+       store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
 
        event_trigger_unlock_commit_regs(trace_file, buffer, event,
                                         entry, irq_flags, pc, regs);
@@ -1057,7 +1054,7 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
        entry = ring_buffer_event_data(event);
        entry->func = (unsigned long)tk->rp.kp.addr;
        entry->ret_ip = (unsigned long)ri->ret_addr;
-       store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize);
+       store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
 
        event_trigger_unlock_commit_regs(trace_file, buffer, event,
                                         entry, irq_flags, pc, regs);
@@ -1203,7 +1200,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
 
        entry->ip = (unsigned long)tk->rp.kp.addr;
        memset(&entry[1], 0, dsize);
-       store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize);
+       store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
        perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
                              head, NULL);
        return 0;
@@ -1239,7 +1236,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
 
        entry->func = (unsigned long)tk->rp.kp.addr;
        entry->ret_ip = (unsigned long)ri->ret_addr;
-       store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize);
+       store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize);
        perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
                              head, NULL);
 }
index 5c262ed6347cee7a497a19c5098c606377faacf1..b6bdd82fa4856f13379e5b1e2f73bf8e7459697f 100644 (file)
 #define TP_FLAG_PROFILE                2
 #define TP_FLAG_REGISTERED     4
 
+/* data_loc: data location, compatible with u32 */
+#define make_data_loc(len, offs)       \
+       (((u32)(len) << 16) | ((u32)(offs) & 0xffff))
+#define get_loc_len(dl)                ((u32)(dl) >> 16)
+#define get_loc_offs(dl)       ((u32)(dl) & 0xffff)
 
-/* data_rloc: data relative location, compatible with u32 */
-#define make_data_rloc(len, roffs)     \
-       (((u32)(len) << 16) | ((u32)(roffs) & 0xffff))
-#define get_rloc_len(dl)               ((u32)(dl) >> 16)
-#define get_rloc_offs(dl)              ((u32)(dl) & 0xffff)
-
-/*
- * Convert data_rloc to data_loc:
- *  data_rloc stores the offset from data_rloc itself, but data_loc
- *  stores the offset from event entry.
- */
-#define convert_rloc_to_loc(dl, offs)  ((u32)(dl) + (offs))
-
-static nokprobe_inline void *get_rloc_data(u32 *dl)
-{
-       return (u8 *)dl + get_rloc_offs(*dl);
-}
-
-/* For data_loc conversion */
 static nokprobe_inline void *get_loc_data(u32 *dl, void *ent)
 {
-       return (u8 *)ent + get_rloc_offs(*dl);
+       return (u8 *)ent + get_loc_offs(*dl);
 }
 
 /* Printing function type */
index c8a5272abf015dd6e3d5db6487137a9c3b277bb8..3b4aba6f84cc4a273ffe9aa07e227d7a19ff593e 100644 (file)
@@ -48,24 +48,28 @@ fetch_apply_bitfield(struct fetch_insn *code, void *buf)
        }
 }
 
-/* Define this for each callsite */
+/*
+ * This must be defined for each callsite.
+ * Return consumed dynamic data size (>= 0), or error (< 0).
+ * If dest is NULL, don't store result and return required dynamic data size.
+ */
 static int
 process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs,
-                  void *dest, bool pre);
+                  void *dest, void *base);
 
 /* Sum up total data length for dynamic arraies (strings) */
 static nokprobe_inline int
 __get_data_size(struct trace_probe *tp, struct pt_regs *regs)
 {
        struct probe_arg *arg;
-       int i, ret = 0;
-       u32 len;
+       int i, len, ret = 0;
 
        for (i = 0; i < tp->nr_args; i++) {
                arg = tp->args + i;
                if (unlikely(arg->dynamic)) {
-                       process_fetch_insn(arg->code, regs, &len, true);
-                       ret += len;
+                       len = process_fetch_insn(arg->code, regs, NULL, NULL);
+                       if (len > 0)
+                               ret += len;
                }
        }
 
@@ -74,34 +78,26 @@ __get_data_size(struct trace_probe *tp, struct pt_regs *regs)
 
 /* Store the value of each argument */
 static nokprobe_inline void
-store_trace_args(int ent_size, struct trace_probe *tp, struct pt_regs *regs,
-                u8 *data, int maxlen)
+store_trace_args(void *data, struct trace_probe *tp, struct pt_regs *regs,
+                int header_size, int maxlen)
 {
        struct probe_arg *arg;
-       u32 end = tp->size;
-       u32 *dl;        /* Data (relative) location */
-       int i;
+       void *base = data - header_size;
+       void *dyndata = data + tp->size;
+       u32 *dl;        /* Data location */
+       int ret, i;
 
        for (i = 0; i < tp->nr_args; i++) {
                arg = tp->args + i;
-               if (unlikely(arg->dynamic)) {
-                       /*
-                        * First, we set the relative location and
-                        * maximum data length to *dl
-                        */
-                       dl = (u32 *)(data + arg->offset);
-                       *dl = make_data_rloc(maxlen, end - arg->offset);
-                       /* Then try to fetch string or dynamic array data */
-                       process_fetch_insn(arg->code, regs, dl, false);
-                       /* Reduce maximum length */
-                       end += get_rloc_len(*dl);
-                       maxlen -= get_rloc_len(*dl);
-                       /* Trick here, convert data_rloc to data_loc */
-                       *dl = convert_rloc_to_loc(*dl, ent_size + arg->offset);
-               } else
-                       /* Just fetching data normally */
-                       process_fetch_insn(arg->code, regs, data + arg->offset,
-                                          false);
+               dl = data + arg->offset;
+               /* Point the dynamic data area if needed */
+               if (unlikely(arg->dynamic))
+                       *dl = make_data_loc(maxlen, dyndata - base);
+               ret = process_fetch_insn(arg->code, regs, dl, base);
+               if (unlikely(ret < 0 && arg->dynamic))
+                       *dl = make_data_loc(0, dyndata - base);
+               else
+                       dyndata += ret;
        }
 }
 
index 7772fec84c12915d438cafeb34373b4adf6cf911..08ad51c8ebc03ad632d29ecb2496e5cf5bbf4351 100644 (file)
@@ -111,43 +111,38 @@ probe_user_read(void *dest, void *src, size_t size)
  * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max
  * length and relative data location.
  */
-static nokprobe_inline void
-fetch_store_string(unsigned long addr, void *dest)
+static nokprobe_inline int
+fetch_store_string(unsigned long addr, void *dest, void *base)
 {
        long ret;
-       u32 rloc = *(u32 *)dest;
-       int maxlen  = get_rloc_len(rloc);
-       u8 *dst = get_rloc_data(dest);
+       u32 loc = *(u32 *)dest;
+       int maxlen  = get_loc_len(loc);
+       u8 *dst = get_loc_data(dest, base);
        void __user *src = (void __force __user *) addr;
 
-       if (!maxlen)
-               return;
+       if (unlikely(!maxlen))
+               return -ENOMEM;
 
        ret = strncpy_from_user(dst, src, maxlen);
-       if (ret == maxlen)
-               dst[--ret] = '\0';
-
-       if (ret < 0) {  /* Failed to fetch string */
-               ((u8 *)get_rloc_data(dest))[0] = '\0';
-               *(u32 *)dest = make_data_rloc(0, get_rloc_offs(rloc));
-       } else {
-               *(u32 *)dest = make_data_rloc(ret, get_rloc_offs(rloc));
+       if (ret >= 0) {
+               if (ret == maxlen)
+                       dst[ret - 1] = '\0';
+               *(u32 *)dest = make_data_loc(ret, (void *)dst - base);
        }
+
+       return ret;
 }
 
 /* Return the length of string -- including null terminal byte */
-static nokprobe_inline void
-fetch_store_strlen(unsigned long addr, void *dest)
+static nokprobe_inline int
+fetch_store_strlen(unsigned long addr)
 {
        int len;
        void __user *vaddr = (void __force __user *) addr;
 
        len = strnlen_user(vaddr, MAX_STRING_SIZE);
 
-       if (len == 0 || len > MAX_STRING_SIZE)  /* Failed to check length */
-               *(u32 *)dest = 0;
-       else
-               *(u32 *)dest = len;
+       return (len > MAX_STRING_SIZE) ? 0 : len;
 }
 
 static unsigned long translate_user_vaddr(unsigned long file_offset)
@@ -164,10 +159,10 @@ static unsigned long translate_user_vaddr(unsigned long file_offset)
 /* Note that we don't verify it, since the code does not come from user space */
 static int
 process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
-                  bool pre)
+                  void *base)
 {
        unsigned long val;
-       int ret;
+       int ret = 0;
 
        /* 1st stage: get value from context */
        switch (code->op) {
@@ -204,18 +199,22 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
        }
 
        /* 3rd stage: store value to buffer */
+       if (unlikely(!dest)) {
+               if (code->op == FETCH_OP_ST_STRING)
+                       return fetch_store_strlen(val + code->offset);
+               else
+                       return -EILSEQ;
+       }
+
        switch (code->op) {
        case FETCH_OP_ST_RAW:
                fetch_store_raw(val, code, dest);
                break;
        case FETCH_OP_ST_MEM:
-               probe_user_read(dest, (void *)val + code->offset, code->size);
+               probe_kernel_read(dest, (void *)val + code->offset, code->size);
                break;
        case FETCH_OP_ST_STRING:
-               if (pre)
-                       fetch_store_strlen(val + code->offset, dest);
-               else
-                       fetch_store_string(val + code->offset, dest);
+               ret = fetch_store_string(val + code->offset, dest, base);
                break;
        default:
                return -EILSEQ;
@@ -228,7 +227,7 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
                code++;
        }
 
-       return code->op == FETCH_OP_END ? 0 : -EILSEQ;
+       return code->op == FETCH_OP_END ? ret : -EILSEQ;
 }
 NOKPROBE_SYMBOL(process_fetch_insn)
 
@@ -1300,7 +1299,7 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs)
        esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
 
        ucb = uprobe_buffer_get();
-       store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
+       store_trace_args(ucb->buf, &tu->tp, regs, esize, dsize);
 
        if (tu->tp.flags & TP_FLAG_TRACE)
                ret |= uprobe_trace_func(tu, regs, ucb, dsize);
@@ -1335,7 +1334,7 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
        esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
 
        ucb = uprobe_buffer_get();
-       store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
+       store_trace_args(ucb->buf, &tu->tp, regs, esize, dsize);
 
        if (tu->tp.flags & TP_FLAG_TRACE)
                uretprobe_trace_func(tu, func, regs, ucb, dsize);