tracing/probes: Implement 'memory' fetch method for uprobes
[linux-2.6-block.git] / kernel / trace / trace_probe.c
index 77aa7d18821ec13a2628ff6e594cec5989a9e6ce..8f7a2b6d389d120a58ba8e5bc63bc5502f35c0cd 100644 (file)
@@ -103,146 +103,12 @@ DEFINE_BASIC_FETCH_FUNCS(retval)
 #define fetch_retval_string            NULL
 #define fetch_retval_string_size       NULL
 
-#define DEFINE_FETCH_memory(type)                                      \
-__kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,     \
-                                         void *addr, void *dest)       \
-{                                                                      \
-       type retval;                                                    \
-       if (probe_kernel_address(addr, retval))                         \
-               *(type *)dest = 0;                                      \
-       else                                                            \
-               *(type *)dest = retval;                                 \
-}
-DEFINE_BASIC_FETCH_FUNCS(memory)
-/*
- * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max
- * length and relative data location.
- */
-__kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
-                                                     void *addr, void *dest)
-{
-       long ret;
-       int maxlen = get_rloc_len(*(u32 *)dest);
-       u8 *dst = get_rloc_data(dest);
-       u8 *src = addr;
-       mm_segment_t old_fs = get_fs();
-
-       if (!maxlen)
-               return;
-
-       /*
-        * Try to get string again, since the string can be changed while
-        * probing.
-        */
-       set_fs(KERNEL_DS);
-       pagefault_disable();
-
-       do
-               ret = __copy_from_user_inatomic(dst++, src++, 1);
-       while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen);
-
-       dst[-1] = '\0';
-       pagefault_enable();
-       set_fs(old_fs);
-
-       if (ret < 0) {  /* Failed to fetch string */
-               ((u8 *)get_rloc_data(dest))[0] = '\0';
-               *(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest));
-       } else {
-               *(u32 *)dest = make_data_rloc(src - (u8 *)addr,
-                                             get_rloc_offs(*(u32 *)dest));
-       }
-}
-
-/* Return the length of string -- including null terminal byte */
-__kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
-                                                       void *addr, void *dest)
-{
-       mm_segment_t old_fs;
-       int ret, len = 0;
-       u8 c;
-
-       old_fs = get_fs();
-       set_fs(KERNEL_DS);
-       pagefault_disable();
-
-       do {
-               ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1);
-               len++;
-       } while (c && ret == 0 && len < MAX_STRING_SIZE);
-
-       pagefault_enable();
-       set_fs(old_fs);
-
-       if (ret < 0)    /* Failed to check the length */
-               *(u32 *)dest = 0;
-       else
-               *(u32 *)dest = len;
-}
-
-/* Memory fetching by symbol */
-struct symbol_cache {
-       char            *symbol;
-       long            offset;
-       unsigned long   addr;
-};
-
-static unsigned long update_symbol_cache(struct symbol_cache *sc)
-{
-       sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol);
-
-       if (sc->addr)
-               sc->addr += sc->offset;
-
-       return sc->addr;
-}
-
-static void free_symbol_cache(struct symbol_cache *sc)
-{
-       kfree(sc->symbol);
-       kfree(sc);
-}
-
-static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset)
-{
-       struct symbol_cache *sc;
-
-       if (!sym || strlen(sym) == 0)
-               return NULL;
-
-       sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL);
-       if (!sc)
-               return NULL;
-
-       sc->symbol = kstrdup(sym, GFP_KERNEL);
-       if (!sc->symbol) {
-               kfree(sc);
-               return NULL;
-       }
-       sc->offset = offset;
-       update_symbol_cache(sc);
-
-       return sc;
-}
-
-#define DEFINE_FETCH_symbol(type)                                      \
-__kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,     \
-                                         void *data, void *dest)       \
-{                                                                      \
-       struct symbol_cache *sc = data;                                 \
-       if (sc->addr)                                                   \
-               fetch_memory_##type(regs, (void *)sc->addr, dest);      \
-       else                                                            \
-               *(type *)dest = 0;                                      \
-}
-DEFINE_BASIC_FETCH_FUNCS(symbol)
-DEFINE_FETCH_symbol(string)
-DEFINE_FETCH_symbol(string_size)
-
 /* Dereference memory access function */
 struct deref_fetch_param {
        struct fetch_param      orig;
        long                    offset;
+       fetch_func_t            fetch;
+       fetch_func_t            fetch_size;
 };
 
 #define DEFINE_FETCH_deref(type)                                       \
@@ -254,13 +120,26 @@ __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs, \
        call_fetch(&dprm->orig, regs, &addr);                           \
        if (addr) {                                                     \
                addr += dprm->offset;                                   \
-               fetch_memory_##type(regs, (void *)addr, dest);          \
+               dprm->fetch(regs, (void *)addr, dest);                  \
        } else                                                          \
                *(type *)dest = 0;                                      \
 }
 DEFINE_BASIC_FETCH_FUNCS(deref)
 DEFINE_FETCH_deref(string)
-DEFINE_FETCH_deref(string_size)
+
+__kprobes void FETCH_FUNC_NAME(deref, string_size)(struct pt_regs *regs,
+                                                  void *data, void *dest)
+{
+       struct deref_fetch_param *dprm = data;
+       unsigned long addr;
+
+       call_fetch(&dprm->orig, regs, &addr);
+       if (addr && dprm->fetch_size) {
+               addr += dprm->offset;
+               dprm->fetch_size(regs, (void *)addr, dest);
+       } else
+               *(string_size *)dest = 0;
+}
 
 static __kprobes void update_deref_fetch_param(struct deref_fetch_param *data)
 {
@@ -536,6 +415,9 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t,
                                return -ENOMEM;
 
                        dprm->offset = offset;
+                       dprm->fetch = t->fetch[FETCH_MTD_memory];
+                       dprm->fetch_size = get_fetch_size_function(t,
+                                                       dprm->fetch, ftbl);
                        ret = parse_probe_arg(arg, t2, &dprm->orig, is_return,
                                                        is_kprobe);
                        if (ret)