These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / tools / perf / util / dwarf-aux.c
index c34e024..a509aa8 100644 (file)
@@ -139,10 +139,26 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
 bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
 {
        const char *name;
+
        name = dwarf_diename(dw_die);
        return name ? (strcmp(tname, name) == 0) : false;
 }
 
+/**
+ * die_match_name - Match diename and glob
+ * @dw_die: a DIE
+ * @glob: a string of target glob pattern
+ *
+ * Glob matching the name of @dw_die and @glob. Return false if matching fail.
+ */
+bool die_match_name(Dwarf_Die *dw_die, const char *glob)
+{
+       const char *name;
+
+       name = dwarf_diename(dw_die);
+       return name ? strglobmatch(name, glob) : false;
+}
+
 /**
  * die_get_call_lineno - Get callsite line number of inline-function instance
  * @in_die: a DIE of an inlined function instance
@@ -417,6 +433,43 @@ struct __addr_die_search_param {
        Dwarf_Die       *die_mem;
 };
 
+static int __die_search_func_tail_cb(Dwarf_Die *fn_die, void *data)
+{
+       struct __addr_die_search_param *ad = data;
+       Dwarf_Addr addr = 0;
+
+       if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
+           !dwarf_highpc(fn_die, &addr) &&
+           addr == ad->addr) {
+               memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
+               return DWARF_CB_ABORT;
+       }
+       return DWARF_CB_OK;
+}
+
+/**
+ * die_find_tailfunc - Search for a non-inlined function with tail call at
+ * given address
+ * @cu_die: a CU DIE which including @addr
+ * @addr: target address
+ * @die_mem: a buffer for result DIE
+ *
+ * Search for a non-inlined function DIE with tail call at @addr. Stores the
+ * DIE to @die_mem and returns it if found. Returns NULL if failed.
+ */
+Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
+                                   Dwarf_Die *die_mem)
+{
+       struct __addr_die_search_param ad;
+       ad.addr = addr;
+       ad.die_mem = die_mem;
+       /* dwarf_getscopes can't find subprogram. */
+       if (!dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0))
+               return NULL;
+       else
+               return die_mem;
+}
+
 /* die_find callback for non-inlined function search */
 static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
 {
@@ -681,15 +734,18 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
        Dwarf_Lines *lines;
        Dwarf_Line *line;
        Dwarf_Addr addr;
-       const char *fname;
+       const char *fname, *decf = NULL;
        int lineno, ret = 0;
+       int decl = 0, inl;
        Dwarf_Die die_mem, *cu_die;
        size_t nlines, i;
 
        /* Get the CU die */
-       if (dwarf_tag(rt_die) != DW_TAG_compile_unit)
+       if (dwarf_tag(rt_die) != DW_TAG_compile_unit) {
                cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL);
-       else
+               dwarf_decl_line(rt_die, &decl);
+               decf = dwarf_decl_file(rt_die);
+       } else
                cu_die = rt_die;
        if (!cu_die) {
                pr_debug2("Failed to get CU from given DIE.\n");
@@ -714,15 +770,21 @@ int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
                        continue;
                }
                /* Filter lines based on address */
-               if (rt_die != cu_die)
+               if (rt_die != cu_die) {
                        /*
                         * Address filtering
                         * The line is included in given function, and
                         * no inline block includes it.
                         */
-                       if (!dwarf_haspc(rt_die, addr) ||
-                           die_find_inlinefunc(rt_die, addr, &die_mem))
+                       if (!dwarf_haspc(rt_die, addr))
                                continue;
+                       if (die_find_inlinefunc(rt_die, addr, &die_mem)) {
+                               dwarf_decl_line(&die_mem, &inl);
+                               if (inl != decl ||
+                                   decf != dwarf_decl_file(&die_mem))
+                                       continue;
+                       }
+               }
                /* Get source line */
                fname = dwarf_linesrc(line, NULL, NULL);
 
@@ -832,19 +894,17 @@ Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
 /**
  * die_get_typename - Get the name of given variable DIE
  * @vr_die: a variable DIE
- * @buf: a buffer for result type name
- * @len: a max-length of @buf
+ * @buf: a strbuf for result type name
  *
- * Get the name of @vr_die and stores it to @buf. Return the actual length
- * of type name if succeeded. Return -E2BIG if @len is not enough long, and
- * Return -ENOENT if failed to find type name.
+ * Get the name of @vr_die and stores it to @buf. Return 0 if succeeded.
+ * and Return -ENOENT if failed to find type name.
  * Note that the result will stores typedef name if possible, and stores
  * "*(function_type)" if the type is a function pointer.
  */
-int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
+int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)
 {
        Dwarf_Die type;
-       int tag, ret, ret2;
+       int tag, ret;
        const char *tmp = "";
 
        if (__die_get_real_type(vr_die, &type) == NULL)
@@ -855,8 +915,8 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
                tmp = "*";
        else if (tag == DW_TAG_subroutine_type) {
                /* Function pointer */
-               ret = snprintf(buf, len, "(function_type)");
-               return (ret >= len) ? -E2BIG : ret;
+               strbuf_addf(buf, "(function_type)");
+               return 0;
        } else {
                if (!dwarf_diename(&type))
                        return -ENOENT;
@@ -867,39 +927,156 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
                else if (tag == DW_TAG_enumeration_type)
                        tmp = "enum ";
                /* 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;
+               strbuf_addf(buf, "%s%s", tmp, dwarf_diename(&type));
+               return 0;
        }
+       ret = die_get_typename(&type, buf);
+       if (ret == 0)
+               strbuf_addf(buf, "%s", tmp);
+
        return ret;
 }
 
 /**
  * die_get_varname - Get the name and type of given variable DIE
  * @vr_die: a variable DIE
- * @buf: a buffer for type and variable name
- * @len: the max-length of @buf
+ * @buf: a strbuf for type and variable name
  *
  * Get the name and type of @vr_die and stores it in @buf as "type\tname".
  */
-int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
+int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf)
 {
-       int ret, ret2;
+       int ret;
 
-       ret = die_get_typename(vr_die, buf, len);
+       ret = die_get_typename(vr_die, buf);
        if (ret < 0) {
                pr_debug("Failed to get type, make it unknown.\n");
-               ret = snprintf(buf, len, "(unknown_type)");
+               strbuf_addf(buf, "(unknown_type)");
        }
-       if (ret > 0) {
-               ret2 = snprintf(buf + ret, len - ret, "\t%s",
-                               dwarf_diename(vr_die));
-               ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+
+       strbuf_addf(buf, "\t%s", dwarf_diename(vr_die));
+
+       return 0;
+}
+
+/**
+ * die_get_var_innermost_scope - Get innermost scope range of given variable DIE
+ * @sp_die: a subprogram DIE
+ * @vr_die: a variable DIE
+ * @buf: a strbuf for variable byte offset range
+ *
+ * Get the innermost scope range of @vr_die and stores it in @buf as
+ * "@<function_name+[NN-NN,NN-NN]>".
+ */
+static int die_get_var_innermost_scope(Dwarf_Die *sp_die, Dwarf_Die *vr_die,
+                               struct strbuf *buf)
+{
+       Dwarf_Die *scopes;
+       int count;
+       size_t offset = 0;
+       Dwarf_Addr base;
+       Dwarf_Addr start, end;
+       Dwarf_Addr entry;
+       int ret;
+       bool first = true;
+       const char *name;
+
+       ret = dwarf_entrypc(sp_die, &entry);
+       if (ret)
+               return ret;
+
+       name = dwarf_diename(sp_die);
+       if (!name)
+               return -ENOENT;
+
+       count = dwarf_getscopes_die(vr_die, &scopes);
+
+       /* (*SCOPES)[1] is the DIE for the scope containing that scope */
+       if (count <= 1) {
+               ret = -EINVAL;
+               goto out;
        }
+
+       while ((offset = dwarf_ranges(&scopes[1], offset, &base,
+                               &start, &end)) > 0) {
+               start -= entry;
+               end -= entry;
+
+               if (first) {
+                       strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64,
+                               name, start, end);
+                       first = false;
+               } else {
+                       strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64,
+                               start, end);
+               }
+       }
+
+       if (!first)
+               strbuf_addf(buf, "]>");
+
+out:
+       free(scopes);
        return ret;
 }
 
+/**
+ * die_get_var_range - Get byte offset range of given variable DIE
+ * @sp_die: a subprogram DIE
+ * @vr_die: a variable DIE
+ * @buf: a strbuf for type and variable name and byte offset range
+ *
+ * Get the byte offset range of @vr_die and stores it in @buf as
+ * "@<function_name+[NN-NN,NN-NN]>".
+ */
+int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
+{
+       int ret = 0;
+       Dwarf_Addr base;
+       Dwarf_Addr start, end;
+       Dwarf_Addr entry;
+       Dwarf_Op *op;
+       size_t nops;
+       size_t offset = 0;
+       Dwarf_Attribute attr;
+       bool first = true;
+       const char *name;
+
+       ret = dwarf_entrypc(sp_die, &entry);
+       if (ret)
+               return ret;
+
+       name = dwarf_diename(sp_die);
+       if (!name)
+               return -ENOENT;
+
+       if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
+               return -EINVAL;
+
+       while ((offset = dwarf_getlocations(
+                               &attr, offset, &base,
+                               &start, &end, &op, &nops)) > 0) {
+               if (start == 0) {
+                       /* Single Location Descriptions */
+                       ret = die_get_var_innermost_scope(sp_die, vr_die, buf);
+                       return ret;
+               }
+
+               /* Location Lists */
+               start -= entry;
+               end -= entry;
+               if (first) {
+                       strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64,
+                               name, start, end);
+                       first = false;
+               } else {
+                       strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64,
+                               start, end);
+               }
+       }
+
+       if (!first)
+               strbuf_addf(buf, "]>");
+
+       return ret;
+}