]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - tools/perf/util/probe-finder.c
Merge branch 'master' into tk71
[mv-sheeva.git] / tools / perf / util / probe-finder.c
index 32b81f707ff5eb5d950a2d233d6c00c6b90fedb2..ab83b6ac5d657c80af1e67790c1ace15d7592578 100644 (file)
@@ -116,6 +116,126 @@ static void line_list__free(struct list_head *head)
        }
 }
 
+/* Dwarf FL wrappers */
+static char *debuginfo_path;   /* Currently dummy */
+
+static const Dwfl_Callbacks offline_callbacks = {
+       .find_debuginfo = dwfl_standard_find_debuginfo,
+       .debuginfo_path = &debuginfo_path,
+
+       .section_address = dwfl_offline_section_address,
+
+       /* We use this table for core files too.  */
+       .find_elf = dwfl_build_id_find_elf,
+};
+
+/* Get a Dwarf from offline image */
+static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
+{
+       Dwfl_Module *mod;
+       Dwarf *dbg = NULL;
+
+       if (!dwflp)
+               return NULL;
+
+       *dwflp = dwfl_begin(&offline_callbacks);
+       if (!*dwflp)
+               return NULL;
+
+       mod = dwfl_report_offline(*dwflp, "", "", fd);
+       if (!mod)
+               goto error;
+
+       dbg = dwfl_module_getdwarf(mod, bias);
+       if (!dbg) {
+error:
+               dwfl_end(*dwflp);
+               *dwflp = NULL;
+       }
+       return dbg;
+}
+
+#if _ELFUTILS_PREREQ(0, 148)
+/* This method is buggy if elfutils is older than 0.148 */
+static int __linux_kernel_find_elf(Dwfl_Module *mod,
+                                  void **userdata,
+                                  const char *module_name,
+                                  Dwarf_Addr base,
+                                  char **file_name, Elf **elfp)
+{
+       int fd;
+       const char *path = kernel_get_module_path(module_name);
+
+       pr_debug2("Use file %s for %s\n", path, module_name);
+       if (path) {
+               fd = open(path, O_RDONLY);
+               if (fd >= 0) {
+                       *file_name = strdup(path);
+                       return fd;
+               }
+       }
+       /* If failed, try to call standard method */
+       return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
+                                         file_name, elfp);
+}
+
+static const Dwfl_Callbacks kernel_callbacks = {
+       .find_debuginfo = dwfl_standard_find_debuginfo,
+       .debuginfo_path = &debuginfo_path,
+
+       .find_elf = __linux_kernel_find_elf,
+       .section_address = dwfl_linux_kernel_module_section_address,
+};
+
+/* Get a Dwarf from live kernel image */
+static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
+                                         Dwarf_Addr *bias)
+{
+       Dwarf *dbg;
+
+       if (!dwflp)
+               return NULL;
+
+       *dwflp = dwfl_begin(&kernel_callbacks);
+       if (!*dwflp)
+               return NULL;
+
+       /* Load the kernel dwarves: Don't care the result here */
+       dwfl_linux_kernel_report_kernel(*dwflp);
+       dwfl_linux_kernel_report_modules(*dwflp);
+
+       dbg = dwfl_addrdwarf(*dwflp, addr, bias);
+       /* Here, check whether we could get a real dwarf */
+       if (!dbg) {
+               pr_debug("Failed to find kernel dwarf at %lx\n",
+                        (unsigned long)addr);
+               dwfl_end(*dwflp);
+               *dwflp = NULL;
+       }
+       return dbg;
+}
+#else
+/* With older elfutils, this just support kernel module... */
+static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp,
+                                         Dwarf_Addr *bias)
+{
+       int fd;
+       const char *path = kernel_get_module_path("kernel");
+
+       if (!path) {
+               pr_err("Failed to find vmlinux path\n");
+               return NULL;
+       }
+
+       pr_debug2("Use file %s for debuginfo\n", path);
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return NULL;
+
+       return dwfl_init_offline_dwarf(fd, dwflp, bias);
+}
+#endif
+
 /* Dwarf wrappers */
 
 /* Find the realpath of the target file. */
@@ -160,26 +280,44 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
        return name ? (strcmp(tname, name) == 0) : false;
 }
 
-/* Get type die, but skip qualifiers and typedef */
-static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+/* Get type die */
+static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
 {
        Dwarf_Attribute attr;
+
+       if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
+           dwarf_formref_die(&attr, die_mem))
+               return die_mem;
+       else
+               return NULL;
+}
+
+/* Get a type die, but skip qualifiers */
+static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
        int tag;
 
        do {
-               if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL ||
-                   dwarf_formref_die(&attr, die_mem) == NULL)
-                       return NULL;
-
-               tag = dwarf_tag(die_mem);
-               vr_die = die_mem;
+               vr_die = die_get_type(vr_die, die_mem);
+               if (!vr_die)
+                       break;
+               tag = dwarf_tag(vr_die);
        } while (tag == DW_TAG_const_type ||
                 tag == DW_TAG_restrict_type ||
                 tag == DW_TAG_volatile_type ||
-                tag == DW_TAG_shared_type ||
-                tag == DW_TAG_typedef);
+                tag == DW_TAG_shared_type);
+
+       return vr_die;
+}
+
+/* Get a type die, but skip qualifiers and typedef */
+static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+       do {
+               vr_die = __die_get_real_type(vr_die, die_mem);
+       } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
 
-       return die_mem;
+       return vr_die;
 }
 
 static bool die_is_signed_type(Dwarf_Die *tp_die)
@@ -320,25 +458,35 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
        return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
 }
 
+struct __find_variable_param {
+       const char *name;
+       Dwarf_Addr addr;
+};
+
 static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
 {
-       const char *name = data;
+       struct __find_variable_param *fvp = data;
        int tag;
 
        tag = dwarf_tag(die_mem);
        if ((tag == DW_TAG_formal_parameter ||
             tag == DW_TAG_variable) &&
-           die_compare_name(die_mem, name))
+           die_compare_name(die_mem, fvp->name))
                return DIE_FIND_CB_FOUND;
 
-       return DIE_FIND_CB_CONTINUE;
+       if (dwarf_haspc(die_mem, fvp->addr))
+               return DIE_FIND_CB_CONTINUE;
+       else
+               return DIE_FIND_CB_SIBLING;
 }
 
-/* Find a variable called 'name' */
-static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name,
-                                   Dwarf_Die *die_mem)
+/* Find a variable called 'name' at given address */
+static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
+                                      Dwarf_Addr addr, Dwarf_Die *die_mem)
 {
-       return die_find_child(sp_die, __die_find_variable_cb, (void *)name,
+       struct __find_variable_param fvp = { .name = name, .addr = addr};
+
+       return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
                              die_mem);
 }
 
@@ -361,6 +509,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
                              die_mem);
 }
 
+/* Get the name of given variable DIE */
+static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
+{
+       Dwarf_Die type;
+       int tag, ret, ret2;
+       const char *tmp = "";
+
+       if (__die_get_real_type(vr_die, &type) == NULL)
+               return -ENOENT;
+
+       tag = dwarf_tag(&type);
+       if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
+               tmp = "*";
+       else if (tag == DW_TAG_subroutine_type) {
+               /* Function pointer */
+               ret = snprintf(buf, len, "(function_type)");
+               return (ret >= len) ? -E2BIG : ret;
+       } else {
+               if (!dwarf_diename(&type))
+                       return -ENOENT;
+               if (tag == DW_TAG_union_type)
+                       tmp = "union ";
+               else if (tag == DW_TAG_structure_type)
+                       tmp = "struct ";
+               /* Write a base name */
+               ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
+               return (ret >= len) ? -E2BIG : ret;
+       }
+       ret = die_get_typename(&type, buf, len);
+       if (ret > 0) {
+               ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
+               ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+       }
+       return ret;
+}
+
+/* Get the name and type of given variable DIE, stored as "type\tname" */
+static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
+{
+       int ret, ret2;
+
+       ret = die_get_typename(vr_die, buf, len);
+       if (ret < 0) {
+               pr_debug("Failed to get type, make it unknown.\n");
+               ret = snprintf(buf, len, "(unknown_type)");
+       }
+       if (ret > 0) {
+               ret2 = snprintf(buf + ret, len - ret, "\t%s",
+                               dwarf_diename(vr_die));
+               ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+       }
+       return ret;
+}
+
 /*
  * Probe finder related functions
  */
@@ -374,8 +576,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
        return ref;
 }
 
-/* Show a location */
-static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
+/*
+ * Convert a location into trace_arg.
+ * If tvar == NULL, this just checks variable can be converted.
+ */
+static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
+                                    Dwarf_Op *fb_ops,
+                                    struct probe_trace_arg *tvar)
 {
        Dwarf_Attribute attr;
        Dwarf_Op *op;
@@ -384,20 +591,23 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
        Dwarf_Word offs = 0;
        bool ref = false;
        const char *regs;
-       struct probe_trace_arg *tvar = pf->tvar;
        int ret;
 
+       if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
+               goto static_var;
+
        /* TODO: handle more than 1 exprs */
        if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
-           dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
+           dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
            nops == 0) {
                /* TODO: Support const_value */
-               pr_err("Failed to find the location of %s at this address.\n"
-                      " Perhaps, it has been optimized out.\n", pf->pvar->var);
                return -ENOENT;
        }
 
        if (op->atom == DW_OP_addr) {
+static_var:
+               if (!tvar)
+                       return 0;
                /* Static variables on memory (not stack), make @varname */
                ret = strlen(dwarf_diename(vr_die));
                tvar->value = zalloc(ret + 2);
@@ -412,14 +622,11 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
 
        /* If this is based on frame buffer, set the offset */
        if (op->atom == DW_OP_fbreg) {
-               if (pf->fb_ops == NULL) {
-                       pr_warning("The attribute of frame base is not "
-                                  "supported.\n");
+               if (fb_ops == NULL)
                        return -ENOTSUP;
-               }
                ref = true;
                offs = op->number;
-               op = &pf->fb_ops[0];
+               op = &fb_ops[0];
        }
 
        if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
@@ -435,13 +642,18 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
        } else if (op->atom == DW_OP_regx) {
                regn = op->number;
        } else {
-               pr_warning("DW_OP %x is not supported.\n", op->atom);
+               pr_debug("DW_OP %x is not supported.\n", op->atom);
                return -ENOTSUP;
        }
 
+       if (!tvar)
+               return 0;
+
        regs = get_arch_regstr(regn);
        if (!regs) {
-               pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn);
+               /* This should be a bug in DWARF or this tool */
+               pr_warning("Mapping for the register number %u "
+                          "missing on this architecture.\n", regn);
                return -ERANGE;
        }
 
@@ -487,13 +699,14 @@ static int convert_variable_type(Dwarf_Die *vr_die,
                if (ret != DW_TAG_pointer_type &&
                    ret != DW_TAG_array_type) {
                        pr_warning("Failed to cast into string: "
-                                  "%s(%s) is not a pointer nor array.",
+                                  "%s(%s) is not a pointer nor array.\n",
                                   dwarf_diename(vr_die), dwarf_diename(&type));
                        return -EINVAL;
                }
                if (ret == DW_TAG_pointer_type) {
                        if (die_get_real_type(&type, &type) == NULL) {
-                               pr_warning("Failed to get a type information.");
+                               pr_warning("Failed to get a type"
+                                          " information.\n");
                                return -ENOENT;
                        }
                        while (*ref_ptr)
@@ -508,7 +721,7 @@ static int convert_variable_type(Dwarf_Die *vr_die,
                if (!die_compare_name(&type, "char") &&
                    !die_compare_name(&type, "unsigned char")) {
                        pr_warning("Failed to cast into string: "
-                                  "%s is not (unsigned) char *.",
+                                  "%s is not (unsigned) char *.\n",
                                   dwarf_diename(vr_die));
                        return -EINVAL;
                }
@@ -618,8 +831,8 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
                        return -EINVAL;
                }
                if (field->name[0] == '[') {
-                       pr_err("Semantic error: %s is not a pointor nor array.",
-                              varname);
+                       pr_err("Semantic error: %s is not a pointor"
+                              " nor array.\n", varname);
                        return -EINVAL;
                }
                if (field->ref) {
@@ -666,8 +879,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
        pr_debug("Converting variable %s into trace event.\n",
                 dwarf_diename(vr_die));
 
-       ret = convert_variable_location(vr_die, pf);
-       if (ret == 0 && pf->pvar->field) {
+       ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
+                                       pf->tvar);
+       if (ret == -ENOENT)
+               pr_err("Failed to find the location of %s at this address.\n"
+                      " Perhaps, it has been optimized out.\n", pf->pvar->var);
+       else if (ret == -ENOTSUP)
+               pr_err("Sorry, we don't support this variable location yet.\n");
+       else if (pf->pvar->field) {
                ret = convert_variable_fields(vr_die, pf->pvar->var,
                                              pf->pvar->field, &pf->tvar->ref,
                                              &die_mem);
@@ -722,85 +941,87 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
        pr_debug("Searching '%s' variable in context.\n",
                 pf->pvar->var);
        /* Search child die for local variables and parameters. */
-       if (die_find_variable(sp_die, pf->pvar->var, &vr_die))
+       if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
                ret = convert_variable(&vr_die, pf);
        else {
                /* Search upper class */
                nscopes = dwarf_getscopes_die(sp_die, &scopes);
-               if (nscopes > 0) {
-                       ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var,
-                                               0, NULL, 0, 0, &vr_die);
-                       if (ret >= 0)
+               while (nscopes-- > 1) {
+                       pr_debug("Searching variables in %s\n",
+                                dwarf_diename(&scopes[nscopes]));
+                       /* We should check this scope, so give dummy address */
+                       if (die_find_variable_at(&scopes[nscopes],
+                                                pf->pvar->var, 0,
+                                                &vr_die)) {
                                ret = convert_variable(&vr_die, pf);
-                       else
-                               ret = -ENOENT;
+                               goto found;
+                       }
+               }
+               if (scopes)
                        free(scopes);
-               } else
-                       ret = -ENOENT;
+               ret = -ENOENT;
        }
+found:
        if (ret < 0)
                pr_warning("Failed to find '%s' in this function.\n",
                           pf->pvar->var);
        return ret;
 }
 
-/* Show a probe point to output buffer */
-static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
+/* Convert subprogram DIE to trace point */
+static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
+                                 bool retprobe, struct probe_trace_point *tp)
 {
-       struct probe_trace_event *tev;
        Dwarf_Addr eaddr;
-       Dwarf_Die die_mem;
        const char *name;
-       int ret, i;
-       Dwarf_Attribute fb_attr;
-       size_t nops;
-
-       if (pf->ntevs == pf->max_tevs) {
-               pr_warning("Too many( > %d) probe point found.\n",
-                          pf->max_tevs);
-               return -ERANGE;
-       }
-       tev = &pf->tevs[pf->ntevs++];
-
-       /* If no real subprogram, find a real one */
-       if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
-               sp_die = die_find_real_subprogram(&pf->cu_die,
-                                                pf->addr, &die_mem);
-               if (!sp_die) {
-                       pr_warning("Failed to find probe point in any "
-                                  "functions.\n");
-                       return -ENOENT;
-               }
-       }
 
        /* Copy the name of probe point */
        name = dwarf_diename(sp_die);
        if (name) {
                if (dwarf_entrypc(sp_die, &eaddr) != 0) {
-                       pr_warning("Failed to get entry pc of %s\n",
+                       pr_warning("Failed to get entry address of %s\n",
                                   dwarf_diename(sp_die));
                        return -ENOENT;
                }
-               tev->point.symbol = strdup(name);
-               if (tev->point.symbol == NULL)
+               tp->symbol = strdup(name);
+               if (tp->symbol == NULL)
                        return -ENOMEM;
-               tev->point.offset = (unsigned long)(pf->addr - eaddr);
+               tp->offset = (unsigned long)(paddr - eaddr);
        } else
                /* This function has no name. */
-               tev->point.offset = (unsigned long)pf->addr;
+               tp->offset = (unsigned long)paddr;
 
        /* Return probe must be on the head of a subprogram */
-       if (pf->pev->point.retprobe) {
-               if (tev->point.offset != 0) {
+       if (retprobe) {
+               if (eaddr != paddr) {
                        pr_warning("Return probe must be on the head of"
-                                  " a real function\n");
+                                  " a real function.\n");
                        return -EINVAL;
                }
-               tev->point.retprobe = true;
+               tp->retprobe = true;
        }
 
-       pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
-                tev->point.offset);
+       return 0;
+}
+
+/* Call probe_finder callback with real subprogram DIE */
+static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+       Dwarf_Die die_mem;
+       Dwarf_Attribute fb_attr;
+       size_t nops;
+       int ret;
+
+       /* If no real subprogram, find a real one */
+       if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
+               sp_die = die_find_real_subprogram(&pf->cu_die,
+                                                 pf->addr, &die_mem);
+               if (!sp_die) {
+                       pr_warning("Failed to find probe point in any "
+                                  "functions.\n");
+                       return -ENOENT;
+               }
+       }
 
        /* Get the frame base attribute/ops */
        dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
@@ -813,29 +1034,20 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
                Dwarf_Frame *frame;
                if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 ||
                    dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
-                       pr_warning("Failed to get CFA on 0x%jx\n",
+                       pr_warning("Failed to get call frame on 0x%jx\n",
                                   (uintmax_t)pf->addr);
                        return -ENOENT;
                }
 #endif
        }
 
-       /* Find each argument */
-       tev->nargs = pf->pev->nargs;
-       tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
-       if (tev->args == NULL)
-               return -ENOMEM;
-       for (i = 0; i < pf->pev->nargs; i++) {
-               pf->pvar = &pf->pev->args[i];
-               pf->tvar = &tev->args[i];
-               ret = find_variable(sp_die, pf);
-               if (ret != 0)
-                       return ret;
-       }
+       /* Call finder's callback handler */
+       ret = pf->callback(sp_die, pf);
 
        /* *pf->fb_ops will be cached in libdw. Don't free it. */
        pf->fb_ops = NULL;
-       return 0;
+
+       return ret;
 }
 
 /* Find probe point from its line number */
@@ -849,7 +1061,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
        int ret = 0;
 
        if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
-               pr_warning("No source lines found in this CU.\n");
+               pr_warning("No source lines found.\n");
                return -ENOENT;
        }
 
@@ -871,7 +1083,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
                         (int)i, lineno, (uintmax_t)addr);
                pf->addr = addr;
 
-               ret = convert_probe_point(NULL, pf);
+               ret = call_probe_finder(NULL, pf);
                /* Continuing, because target line might be inlined. */
        }
        return ret;
@@ -951,7 +1163,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
        }
 
        if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
-               pr_warning("No source lines found in this CU.\n");
+               pr_warning("No source lines found.\n");
                return -ENOENT;
        }
 
@@ -984,7 +1196,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
                         (int)i, lineno, (unsigned long long)addr);
                pf->addr = addr;
 
-               ret = convert_probe_point(sp_die, pf);
+               ret = call_probe_finder(sp_die, pf);
                /* Continuing, because target line might be inlined. */
        }
        /* TODO: deallocate lines, but how? */
@@ -1009,7 +1221,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
        else {
                /* Get probe address */
                if (dwarf_entrypc(in_die, &addr) != 0) {
-                       pr_warning("Failed to get entry pc of %s.\n",
+                       pr_warning("Failed to get entry address of %s.\n",
                                   dwarf_diename(in_die));
                        param->retval = -ENOENT;
                        return DWARF_CB_ABORT;
@@ -1019,7 +1231,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
                pr_debug("found inline addr: 0x%jx\n",
                         (uintmax_t)pf->addr);
 
-               param->retval = convert_probe_point(in_die, pf);
+               param->retval = call_probe_finder(in_die, pf);
                if (param->retval < 0)
                        return DWARF_CB_ABORT;
        }
@@ -1050,14 +1262,14 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
                        param->retval = find_probe_point_lazy(sp_die, pf);
                else {
                        if (dwarf_entrypc(sp_die, &pf->addr) != 0) {
-                               pr_warning("Failed to get entry pc of %s.\n",
-                                          dwarf_diename(sp_die));
+                               pr_warning("Failed to get entry address of "
+                                          "%s.\n", dwarf_diename(sp_die));
                                param->retval = -ENOENT;
                                return DWARF_CB_ABORT;
                        }
                        pf->addr += pp->offset;
                        /* TODO: Check the address in this function */
-                       param->retval = convert_probe_point(sp_die, pf);
+                       param->retval = call_probe_finder(sp_die, pf);
                }
        } else {
                struct dwarf_callback_param _param = {.data = (void *)pf,
@@ -1079,90 +1291,277 @@ static int find_probe_point_by_func(struct probe_finder *pf)
        return _param.retval;
 }
 
-/* Find probe_trace_events specified by perf_probe_event from debuginfo */
-int find_probe_trace_events(int fd, struct perf_probe_event *pev,
-                            struct probe_trace_event **tevs, int max_tevs)
+/* Find probe points from debuginfo */
+static int find_probes(int fd, struct probe_finder *pf)
 {
-       struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
-       struct perf_probe_point *pp = &pev->point;
+       struct perf_probe_point *pp = &pf->pev->point;
        Dwarf_Off off, noff;
        size_t cuhl;
        Dwarf_Die *diep;
-       Dwarf *dbg;
+       Dwarf *dbg = NULL;
+       Dwfl *dwfl;
+       Dwarf_Addr bias;        /* Currently ignored */
        int ret = 0;
 
-       pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
-       if (pf.tevs == NULL)
-               return -ENOMEM;
-       *tevs = pf.tevs;
-       pf.ntevs = 0;
-
-       dbg = dwarf_begin(fd, DWARF_C_READ);
+       dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
        if (!dbg) {
-               pr_warning("No dwarf info found in the vmlinux - "
+               pr_warning("No debug information found in the vmlinux - "
                        "please rebuild with CONFIG_DEBUG_INFO=y.\n");
-               free(pf.tevs);
-               *tevs = NULL;
                return -EBADF;
        }
 
 #if _ELFUTILS_PREREQ(0, 142)
        /* Get the call frame information from this dwarf */
-       pf.cfi = dwarf_getcfi(dbg);
+       pf->cfi = dwarf_getcfi(dbg);
 #endif
 
        off = 0;
-       line_list__init(&pf.lcache);
+       line_list__init(&pf->lcache);
        /* Loop on CUs (Compilation Unit) */
        while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
               ret >= 0) {
                /* Get the DIE(Debugging Information Entry) of this CU */
-               diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die);
+               diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
                if (!diep)
                        continue;
 
                /* Check if target file is included. */
                if (pp->file)
-                       pf.fname = cu_find_realpath(&pf.cu_die, pp->file);
+                       pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
                else
-                       pf.fname = NULL;
+                       pf->fname = NULL;
 
-               if (!pp->file || pf.fname) {
+               if (!pp->file || pf->fname) {
                        if (pp->function)
-                               ret = find_probe_point_by_func(&pf);
+                               ret = find_probe_point_by_func(pf);
                        else if (pp->lazy_line)
-                               ret = find_probe_point_lazy(NULL, &pf);
+                               ret = find_probe_point_lazy(NULL, pf);
                        else {
-                               pf.lno = pp->line;
-                               ret = find_probe_point_by_line(&pf);
+                               pf->lno = pp->line;
+                               ret = find_probe_point_by_line(pf);
                        }
                }
                off = noff;
        }
-       line_list__free(&pf.lcache);
-       dwarf_end(dbg);
+       line_list__free(&pf->lcache);
+       if (dwfl)
+               dwfl_end(dwfl);
 
-       return (ret < 0) ? ret : pf.ntevs;
+       return ret;
+}
+
+/* Add a found probe point into trace event list */
+static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+       struct trace_event_finder *tf =
+                       container_of(pf, struct trace_event_finder, pf);
+       struct probe_trace_event *tev;
+       int ret, i;
+
+       /* Check number of tevs */
+       if (tf->ntevs == tf->max_tevs) {
+               pr_warning("Too many( > %d) probe point found.\n",
+                          tf->max_tevs);
+               return -ERANGE;
+       }
+       tev = &tf->tevs[tf->ntevs++];
+
+       ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
+                                    &tev->point);
+       if (ret < 0)
+               return ret;
+
+       pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
+                tev->point.offset);
+
+       /* Find each argument */
+       tev->nargs = pf->pev->nargs;
+       tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
+       if (tev->args == NULL)
+               return -ENOMEM;
+       for (i = 0; i < pf->pev->nargs; i++) {
+               pf->pvar = &pf->pev->args[i];
+               pf->tvar = &tev->args[i];
+               ret = find_variable(sp_die, pf);
+               if (ret != 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/* Find probe_trace_events specified by perf_probe_event from debuginfo */
+int find_probe_trace_events(int fd, struct perf_probe_event *pev,
+                           struct probe_trace_event **tevs, int max_tevs)
+{
+       struct trace_event_finder tf = {
+                       .pf = {.pev = pev, .callback = add_probe_trace_event},
+                       .max_tevs = max_tevs};
+       int ret;
+
+       /* Allocate result tevs array */
+       *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
+       if (*tevs == NULL)
+               return -ENOMEM;
+
+       tf.tevs = *tevs;
+       tf.ntevs = 0;
+
+       ret = find_probes(fd, &tf.pf);
+       if (ret < 0) {
+               free(*tevs);
+               *tevs = NULL;
+               return ret;
+       }
+
+       return (ret < 0) ? ret : tf.ntevs;
+}
+
+#define MAX_VAR_LEN 64
+
+/* Collect available variables in this scope */
+static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
+{
+       struct available_var_finder *af = data;
+       struct variable_list *vl;
+       char buf[MAX_VAR_LEN];
+       int tag, ret;
+
+       vl = &af->vls[af->nvls - 1];
+
+       tag = dwarf_tag(die_mem);
+       if (tag == DW_TAG_formal_parameter ||
+           tag == DW_TAG_variable) {
+               ret = convert_variable_location(die_mem, af->pf.addr,
+                                               af->pf.fb_ops, NULL);
+               if (ret == 0) {
+                       ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
+                       pr_debug2("Add new var: %s\n", buf);
+                       if (ret > 0)
+                               strlist__add(vl->vars, buf);
+               }
+       }
+
+       if (af->child && dwarf_haspc(die_mem, af->pf.addr))
+               return DIE_FIND_CB_CONTINUE;
+       else
+               return DIE_FIND_CB_SIBLING;
+}
+
+/* Add a found vars into available variables list */
+static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+       struct available_var_finder *af =
+                       container_of(pf, struct available_var_finder, pf);
+       struct variable_list *vl;
+       Dwarf_Die die_mem, *scopes = NULL;
+       int ret, nscopes;
+
+       /* Check number of tevs */
+       if (af->nvls == af->max_vls) {
+               pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
+               return -ERANGE;
+       }
+       vl = &af->vls[af->nvls++];
+
+       ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
+                                    &vl->point);
+       if (ret < 0)
+               return ret;
+
+       pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
+                vl->point.offset);
+
+       /* Find local variables */
+       vl->vars = strlist__new(true, NULL);
+       if (vl->vars == NULL)
+               return -ENOMEM;
+       af->child = true;
+       die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
+
+       /* Find external variables */
+       if (!af->externs)
+               goto out;
+       /* Don't need to search child DIE for externs. */
+       af->child = false;
+       nscopes = dwarf_getscopes_die(sp_die, &scopes);
+       while (nscopes-- > 1)
+               die_find_child(&scopes[nscopes], collect_variables_cb,
+                              (void *)af, &die_mem);
+       if (scopes)
+               free(scopes);
+
+out:
+       if (strlist__empty(vl->vars)) {
+               strlist__delete(vl->vars);
+               vl->vars = NULL;
+       }
+
+       return ret;
+}
+
+/* Find available variables at given probe point */
+int find_available_vars_at(int fd, struct perf_probe_event *pev,
+                          struct variable_list **vls, int max_vls,
+                          bool externs)
+{
+       struct available_var_finder af = {
+                       .pf = {.pev = pev, .callback = add_available_vars},
+                       .max_vls = max_vls, .externs = externs};
+       int ret;
+
+       /* Allocate result vls array */
+       *vls = zalloc(sizeof(struct variable_list) * max_vls);
+       if (*vls == NULL)
+               return -ENOMEM;
+
+       af.vls = *vls;
+       af.nvls = 0;
+
+       ret = find_probes(fd, &af.pf);
+       if (ret < 0) {
+               /* Free vlist for error */
+               while (af.nvls--) {
+                       if (af.vls[af.nvls].point.symbol)
+                               free(af.vls[af.nvls].point.symbol);
+                       if (af.vls[af.nvls].vars)
+                               strlist__delete(af.vls[af.nvls].vars);
+               }
+               free(af.vls);
+               *vls = NULL;
+               return ret;
+       }
+
+       return (ret < 0) ? ret : af.nvls;
 }
 
 /* Reverse search */
-int find_perf_probe_point(int fd, unsigned long addr,
-                         struct perf_probe_point *ppt)
+int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
 {
        Dwarf_Die cudie, spdie, indie;
-       Dwarf *dbg;
+       Dwarf *dbg = NULL;
+       Dwfl *dwfl = NULL;
        Dwarf_Line *line;
-       Dwarf_Addr laddr, eaddr;
+       Dwarf_Addr laddr, eaddr, bias = 0;
        const char *tmp;
        int lineno, ret = 0;
        bool found = false;
 
-       dbg = dwarf_begin(fd, DWARF_C_READ);
-       if (!dbg)
-               return -EBADF;
+       /* Open the live linux kernel */
+       dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
+       if (!dbg) {
+               pr_warning("No debug information found in the vmlinux - "
+                       "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+               ret = -EINVAL;
+               goto end;
+       }
 
+       /* Adjust address with bias */
+       addr += bias;
        /* Find cu die */
-       if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) {
+       if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
+               pr_warning("Failed to find debug information for address %lx\n",
+                          addr);
                ret = -EINVAL;
                goto end;
        }
@@ -1225,7 +1624,8 @@ found:
        }
 
 end:
-       dwarf_end(dbg);
+       if (dwfl)
+               dwfl_end(dwfl);
        if (ret >= 0)
                ret = found ? 1 : 0;
        return ret;
@@ -1286,7 +1686,7 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
 
        line_list__init(&lf->lr->line_list);
        if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) {
-               pr_warning("No source lines found in this CU.\n");
+               pr_warning("No source lines found.\n");
                return -ENOENT;
        }
 
@@ -1358,6 +1758,9 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
        struct line_finder *lf = param->data;
        struct line_range *lr = lf->lr;
 
+       pr_debug("find (%llx) %s\n",
+                (unsigned long long)dwarf_dieoffset(sp_die),
+                dwarf_diename(sp_die));
        if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
            die_compare_name(sp_die, lr->function)) {
                lf->fname = dwarf_decl_file(sp_die);
@@ -1401,12 +1804,14 @@ int find_line_range(int fd, struct line_range *lr)
        Dwarf_Off off = 0, noff;
        size_t cuhl;
        Dwarf_Die *diep;
-       Dwarf *dbg;
+       Dwarf *dbg = NULL;
+       Dwfl *dwfl;
+       Dwarf_Addr bias;        /* Currently ignored */
        const char *comp_dir;
 
-       dbg = dwarf_begin(fd, DWARF_C_READ);
+       dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
        if (!dbg) {
-               pr_warning("No dwarf info found in the vmlinux - "
+               pr_warning("No debug information found in the vmlinux - "
                        "please rebuild with CONFIG_DEBUG_INFO=y.\n");
                return -EBADF;
        }
@@ -1450,8 +1855,7 @@ int find_line_range(int fd, struct line_range *lr)
        }
 
        pr_debug("path: %s\n", lr->path);
-       dwarf_end(dbg);
-
+       dwfl_end(dwfl);
        return (ret < 0) ? ret : lf.found;
 }