perf probe: Enable to put probe inline function call site
[pandora-kernel.git] / tools / perf / util / probe-finder.c
index ddf4d45..69215bf 100644 (file)
@@ -280,6 +280,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
        return name ? (strcmp(tname, name) == 0) : false;
 }
 
+/* Get callsite line number of inline-function instance */
+static int die_get_call_lineno(Dwarf_Die *in_die)
+{
+       Dwarf_Attribute attr;
+       Dwarf_Word ret;
+
+       if (!dwarf_attr(in_die, DW_AT_call_line, &attr))
+               return -ENOENT;
+
+       dwarf_formudata(&attr, &ret);
+       return (int)ret;
+}
+
 /* Get type die */
 static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
 {
@@ -458,6 +471,151 @@ 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);
 }
 
+/* Walker on lines (Note: line number will not be sorted) */
+typedef int (* line_walk_handler_t) (const char *fname, int lineno,
+                                    Dwarf_Addr addr, void *data);
+
+struct __line_walk_param {
+       const char *fname;
+       line_walk_handler_t handler;
+       void *data;
+       int retval;
+};
+
+static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
+{
+       struct __line_walk_param *lw = data;
+       Dwarf_Addr addr;
+       int lineno;
+
+       if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
+               lineno = die_get_call_lineno(in_die);
+               if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
+                       lw->retval = lw->handler(lw->fname, lineno, addr,
+                                                lw->data);
+                       if (lw->retval != 0)
+                               return DIE_FIND_CB_FOUND;
+               }
+       }
+       return DIE_FIND_CB_SIBLING;
+}
+
+/* Walk on lines of blocks included in given DIE */
+static int __die_walk_funclines(Dwarf_Die *sp_die,
+                               line_walk_handler_t handler, void *data)
+{
+       struct __line_walk_param lw = {
+               .handler = handler,
+               .data = data,
+               .retval = 0,
+       };
+       Dwarf_Die die_mem;
+       Dwarf_Addr addr;
+       int lineno;
+
+       /* Handle function declaration line */
+       lw.fname = dwarf_decl_file(sp_die);
+       if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
+           dwarf_entrypc(sp_die, &addr) == 0) {
+               lw.retval = handler(lw.fname, lineno, addr, data);
+               if (lw.retval != 0)
+                       goto done;
+       }
+       die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem);
+done:
+       return lw.retval;
+}
+
+static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
+{
+       struct __line_walk_param *lw = data;
+
+       lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data);
+       if (lw->retval != 0)
+               return DWARF_CB_ABORT;
+
+       return DWARF_CB_OK;
+}
+
+/*
+ * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on
+ * the lines inside the subprogram, otherwise PDIE must be a CU DIE.
+ */
+static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler,
+                         void *data)
+{
+       Dwarf_Lines *lines;
+       Dwarf_Line *line;
+       Dwarf_Addr addr;
+       const char *fname;
+       int lineno, ret = 0;
+       Dwarf_Die die_mem, *cu_die;
+       size_t nlines, i;
+
+       /* Get the CU die */
+       if (dwarf_tag(pdie) == DW_TAG_subprogram)
+               cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL);
+       else
+               cu_die = pdie;
+       if (!cu_die) {
+               pr_debug2("Failed to get CU from subprogram\n");
+               return -EINVAL;
+       }
+
+       /* Get lines list in the CU */
+       if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) {
+               pr_debug2("Failed to get source lines on this CU.\n");
+               return -ENOENT;
+       }
+       pr_debug2("Get %zd lines from this CU\n", nlines);
+
+       /* Walk on the lines on lines list */
+       for (i = 0; i < nlines; i++) {
+               line = dwarf_onesrcline(lines, i);
+               if (line == NULL ||
+                   dwarf_lineno(line, &lineno) != 0 ||
+                   dwarf_lineaddr(line, &addr) != 0) {
+                       pr_debug2("Failed to get line info. "
+                                 "Possible error in debuginfo.\n");
+                       continue;
+               }
+               /* Filter lines based on address */
+               if (pdie != cu_die)
+                       /*
+                        * Address filtering
+                        * The line is included in given function, and
+                        * no inline block includes it.
+                        */
+                       if (!dwarf_haspc(pdie, addr) ||
+                           die_find_inlinefunc(pdie, addr, &die_mem))
+                               continue;
+               /* Get source line */
+               fname = dwarf_linesrc(line, NULL, NULL);
+
+               ret = handler(fname, lineno, addr, data);
+               if (ret != 0)
+                       return ret;
+       }
+
+       /*
+        * Dwarf lines doesn't include function declarations and inlined
+        * subroutines. We have to check functions list or given function.
+        */
+       if (pdie != cu_die)
+               ret = __die_walk_funclines(pdie, handler, data);
+       else {
+               struct __line_walk_param param = {
+                       .handler = handler,
+                       .data = data,
+                       .retval = 0,
+               };
+               dwarf_getfuncs(cu_die, __die_walk_culines_cb, &param, 0);
+               ret = param.retval;
+       }
+
+       return ret;
+}
+
 struct __find_variable_param {
        const char *name;
        Dwarf_Addr addr;
@@ -652,8 +810,8 @@ static_var:
        regs = get_arch_regstr(regn);
        if (!regs) {
                /* This should be a bug in DWARF or this tool */
-               pr_warning("Mapping for DWARF register number %u "
-                          "missing on this architecture.", regn);
+               pr_warning("Mapping for the register number %u "
+                          "missing on this architecture.\n", regn);
                return -ERANGE;
        }
 
@@ -699,13 +857,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)
@@ -720,7 +879,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;
                }
@@ -830,8 +989,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) {
@@ -978,7 +1137,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
        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;
                }
@@ -994,7 +1153,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
        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;
                }
                tp->retprobe = true;
@@ -1033,7 +1192,7 @@ static int call_probe_finder(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;
                }
@@ -1049,43 +1208,26 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
        return ret;
 }
 
-/* Find probe point from its line number */
-static int find_probe_point_by_line(struct probe_finder *pf)
+static int probe_point_line_walker(const char *fname, int lineno,
+                                  Dwarf_Addr addr, void *data)
 {
-       Dwarf_Lines *lines;
-       Dwarf_Line *line;
-       size_t nlines, i;
-       Dwarf_Addr addr;
-       int lineno;
-       int ret = 0;
-
-       if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
-               pr_warning("No source lines found in this CU.\n");
-               return -ENOENT;
-       }
+       struct probe_finder *pf = data;
+       int ret;
 
-       for (i = 0; i < nlines && ret == 0; i++) {
-               line = dwarf_onesrcline(lines, i);
-               if (dwarf_lineno(line, &lineno) != 0 ||
-                   lineno != pf->lno)
-                       continue;
+       if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
+               return 0;
 
-               /* TODO: Get fileno from line, but how? */
-               if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
-                       continue;
+       pf->addr = addr;
+       ret = call_probe_finder(NULL, pf);
 
-               if (dwarf_lineaddr(line, &addr) != 0) {
-                       pr_warning("Failed to get the address of the line.\n");
-                       return -ENOENT;
-               }
-               pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n",
-                        (int)i, lineno, (uintmax_t)addr);
-               pf->addr = addr;
+       /* Continue if no error, because the line will be in inline function */
+       return ret < 0 ?: 0;
+}
 
-               ret = call_probe_finder(NULL, pf);
-               /* Continuing, because target line might be inlined. */
-       }
-       return ret;
+/* Find probe point from its line number */
+static int find_probe_point_by_line(struct probe_finder *pf)
+{
+       return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf);
 }
 
 /* Find lines which match lazy pattern */
@@ -1139,15 +1281,31 @@ out_close:
        return nlines;
 }
 
+static int probe_point_lazy_walker(const char *fname, int lineno,
+                                  Dwarf_Addr addr, void *data)
+{
+       struct probe_finder *pf = data;
+       int ret;
+
+       if (!line_list__has_line(&pf->lcache, lineno) ||
+           strtailcmp(fname, pf->fname) != 0)
+               return 0;
+
+       pr_debug("Probe line found: line:%d addr:0x%llx\n",
+                lineno, (unsigned long long)addr);
+       pf->addr = addr;
+       ret = call_probe_finder(NULL, pf);
+
+       /*
+        * Continue if no error, because the lazy pattern will match
+        * to other lines
+        */
+       return ret < 0 ?: 0;
+}
+
 /* Find probe points from lazy pattern  */
 static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
 {
-       Dwarf_Lines *lines;
-       Dwarf_Line *line;
-       size_t nlines, i;
-       Dwarf_Addr addr;
-       Dwarf_Die die_mem;
-       int lineno;
        int ret = 0;
 
        if (list_empty(&pf->lcache)) {
@@ -1161,45 +1319,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
                        return ret;
        }
 
-       if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
-               pr_warning("No source lines found in this CU.\n");
-               return -ENOENT;
-       }
-
-       for (i = 0; i < nlines && ret >= 0; i++) {
-               line = dwarf_onesrcline(lines, i);
-
-               if (dwarf_lineno(line, &lineno) != 0 ||
-                   !line_list__has_line(&pf->lcache, lineno))
-                       continue;
-
-               /* TODO: Get fileno from line, but how? */
-               if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
-                       continue;
-
-               if (dwarf_lineaddr(line, &addr) != 0) {
-                       pr_debug("Failed to get the address of line %d.\n",
-                                lineno);
-                       continue;
-               }
-               if (sp_die) {
-                       /* Address filtering 1: does sp_die include addr? */
-                       if (!dwarf_haspc(sp_die, addr))
-                               continue;
-                       /* Address filtering 2: No child include addr? */
-                       if (die_find_inlinefunc(sp_die, addr, &die_mem))
-                               continue;
-               }
-
-               pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n",
-                        (int)i, lineno, (unsigned long long)addr);
-               pf->addr = addr;
-
-               ret = call_probe_finder(sp_die, pf);
-               /* Continuing, because target line might be inlined. */
-       }
-       /* TODO: deallocate lines, but how? */
-       return ret;
+       return die_walk_lines(sp_die, probe_point_lazy_walker, pf);
 }
 
 /* Callback parameter with return value */
@@ -1220,7 +1340,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;
@@ -1261,8 +1381,8 @@ 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;
                        }
@@ -1304,7 +1424,7 @@ static int find_probes(int fd, struct probe_finder *pf)
 
        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;
        }
@@ -1549,7 +1669,7 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
        /* Open the live linux kernel */
        dbg = dwfl_init_live_kernel_dwarf(addr, &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");
                ret = -EINVAL;
                goto end;
@@ -1559,7 +1679,8 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
        addr += bias;
        /* Find cu die */
        if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
-               pr_warning("No CU DIE is found at %lx\n", addr);
+               pr_warning("Failed to find debug information for address %lx\n",
+                          addr);
                ret = -EINVAL;
                goto end;
        }
@@ -1642,91 +1763,28 @@ static int line_range_add_line(const char *src, unsigned int lineno,
        return line_list__add_line(&lr->line_list, lineno);
 }
 
-/* Search function declaration lines */
-static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data)
+static int line_range_walk_cb(const char *fname, int lineno,
+                             Dwarf_Addr addr __used,
+                             void *data)
 {
-       struct dwarf_callback_param *param = data;
-       struct line_finder *lf = param->data;
-       const char *src;
-       int lineno;
+       struct line_finder *lf = data;
 
-       src = dwarf_decl_file(sp_die);
-       if (src && strtailcmp(src, lf->fname) != 0)
-               return DWARF_CB_OK;
-
-       if (dwarf_decl_line(sp_die, &lineno) != 0 ||
+       if ((strtailcmp(fname, lf->fname) != 0) ||
            (lf->lno_s > lineno || lf->lno_e < lineno))
-               return DWARF_CB_OK;
+               return 0;
 
-       param->retval = line_range_add_line(src, lineno, lf->lr);
-       if (param->retval < 0)
-               return DWARF_CB_ABORT;
-       return DWARF_CB_OK;
-}
+       if (line_range_add_line(fname, lineno, lf->lr) < 0)
+               return -EINVAL;
 
-static int find_line_range_func_decl_lines(struct line_finder *lf)
-{
-       struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
-       dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, &param, 0);
-       return param.retval;
+       return 0;
 }
 
 /* Find line range from its line number */
 static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
 {
-       Dwarf_Lines *lines;
-       Dwarf_Line *line;
-       size_t nlines, i;
-       Dwarf_Addr addr;
-       int lineno, ret = 0;
-       const char *src;
-       Dwarf_Die die_mem;
-
-       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");
-               return -ENOENT;
-       }
-
-       /* Search probable lines on lines list */
-       for (i = 0; i < nlines; i++) {
-               line = dwarf_onesrcline(lines, i);
-               if (dwarf_lineno(line, &lineno) != 0 ||
-                   (lf->lno_s > lineno || lf->lno_e < lineno))
-                       continue;
-
-               if (sp_die) {
-                       /* Address filtering 1: does sp_die include addr? */
-                       if (dwarf_lineaddr(line, &addr) != 0 ||
-                           !dwarf_haspc(sp_die, addr))
-                               continue;
-
-                       /* Address filtering 2: No child include addr? */
-                       if (die_find_inlinefunc(sp_die, addr, &die_mem))
-                               continue;
-               }
-
-               /* TODO: Get fileno from line, but how? */
-               src = dwarf_linesrc(line, NULL, NULL);
-               if (strtailcmp(src, lf->fname) != 0)
-                       continue;
-
-               ret = line_range_add_line(src, lineno, lf->lr);
-               if (ret < 0)
-                       return ret;
-       }
+       int ret;
 
-       /*
-        * Dwarf lines doesn't include function declarations. We have to
-        * check functions list or given function.
-        */
-       if (sp_die) {
-               src = dwarf_decl_file(sp_die);
-               if (src && dwarf_decl_line(sp_die, &lineno) == 0 &&
-                   (lf->lno_s <= lineno && lf->lno_e >= lineno))
-                       ret = line_range_add_line(src, lineno, lf->lr);
-       } else
-               ret = find_line_range_func_decl_lines(lf);
+       ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf);
 
        /* Update status */
        if (ret >= 0)
@@ -1756,9 +1814,6 @@ 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);
@@ -1809,7 +1864,7 @@ int find_line_range(int fd, struct line_range *lr)
 
        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;
        }