]> git.itanic.dy.fi Git - linux-stable/commitdiff
perf dwarf-aux: Add die_find_variable_by_addr()
authorNamhyung Kim <namhyung@kernel.org>
Thu, 9 Nov 2023 23:59:51 +0000 (15:59 -0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 28 Nov 2023 17:14:53 +0000 (14:14 -0300)
The die_find_variable_by_addr() is to find a variables in the given DIE
using given (PC-relative) address.  Global variables will have a
location expression with DW_OP_addr which has an address so can simply
compare it with the address.

  <1><143a7>: Abbrev Number: 2 (DW_TAG_variable)
      <143a8>   DW_AT_name        : loops_per_jiffy
      <143ac>   DW_AT_type        : <0x1cca>
      <143b0>   DW_AT_external    : 1
      <143b0>   DW_AT_decl_file   : 193
      <143b1>   DW_AT_decl_line   : 213
      <143b2>   DW_AT_location    : 9 byte block: 3 b0 46 41 82 ff ff ff ff
                                     (DW_OP_addr: ffffffff824146b0)

Note that the type-offset should be calculated from the base address of
the global variable.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: linux-toolchains@vger.kernel.org
Cc: linux-trace-devel@vger.kernel.org
Link: https://lore.kernel.org/r/20231110000012.3538610-33-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/dwarf-aux.c
tools/perf/util/dwarf-aux.h

index 652e6e7368a26fe67d27dac70704c6698e60c17b..edd9e407bc74e9f972037bf9978e8d317d27b6d6 100644 (file)
@@ -1250,8 +1250,12 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
 struct find_var_data {
        /* Target instruction address */
        Dwarf_Addr pc;
+       /* Target memory address (for global data) */
+       Dwarf_Addr addr;
        /* Target register */
        unsigned reg;
+       /* Access offset, set for global data */
+       int offset;
 };
 
 /* Max number of registers DW_OP_regN supports */
@@ -1312,6 +1316,81 @@ Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
        };
        return die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem);
 }
+
+/* Only checks direct child DIEs in the given scope */
+static int __die_find_var_addr_cb(Dwarf_Die *die_mem, void *arg)
+{
+       struct find_var_data *data = arg;
+       int tag = dwarf_tag(die_mem);
+       ptrdiff_t off = 0;
+       Dwarf_Attribute attr;
+       Dwarf_Addr base, start, end;
+       Dwarf_Word size;
+       Dwarf_Die type_die;
+       Dwarf_Op *ops;
+       size_t nops;
+
+       if (tag != DW_TAG_variable)
+               return DIE_FIND_CB_SIBLING;
+
+       if (dwarf_attr(die_mem, DW_AT_location, &attr) == NULL)
+               return DIE_FIND_CB_SIBLING;
+
+       while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) {
+               if (ops->atom != DW_OP_addr)
+                       continue;
+
+               if (data->addr < ops->number)
+                       continue;
+
+               if (data->addr == ops->number) {
+                       /* Update offset relative to the start of the variable */
+                       data->offset = 0;
+                       return DIE_FIND_CB_END;
+               }
+
+               if (die_get_real_type(die_mem, &type_die) == NULL)
+                       continue;
+
+               if (dwarf_aggregate_size(&type_die, &size) < 0)
+                       continue;
+
+               if (data->addr >= ops->number + size)
+                       continue;
+
+               /* Update offset relative to the start of the variable */
+               data->offset = data->addr - ops->number;
+               return DIE_FIND_CB_END;
+       }
+       return DIE_FIND_CB_SIBLING;
+}
+
+/**
+ * die_find_variable_by_addr - Find variable located at given address
+ * @sc_die: a scope DIE
+ * @pc: the program address to find
+ * @addr: the data address to find
+ * @die_mem: a buffer to save the resulting DIE
+ * @offset: the offset in the resulting type
+ *
+ * Find the variable DIE located at the given address (in PC-relative mode).
+ * This is usually for global variables.
+ */
+Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die, Dwarf_Addr pc,
+                                    Dwarf_Addr addr, Dwarf_Die *die_mem,
+                                    int *offset)
+{
+       struct find_var_data data = {
+               .pc = pc,
+               .addr = addr,
+       };
+       Dwarf_Die *result;
+
+       result = die_find_child(sc_die, __die_find_var_addr_cb, &data, die_mem);
+       if (result)
+               *offset = data.offset;
+       return result;
+}
 #endif
 
 /*
index b6f430730bd13f19fea7234758005baa7489db84..0ddf61fd3f8beabf49a185f312b095a000f7015e 100644 (file)
@@ -141,6 +141,11 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf);
 Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
                                    Dwarf_Die *die_mem);
 
+/* Find a (global) variable located in the 'addr' */
+Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die, Dwarf_Addr pc,
+                                    Dwarf_Addr addr, Dwarf_Die *die_mem,
+                                    int *offset);
+
 #else /*  HAVE_DWARF_GETLOCATIONS_SUPPORT */
 
 static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
@@ -158,6 +163,15 @@ static inline Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die __maybe_unus
        return NULL;
 }
 
+static inline Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die __maybe_unused,
+                                                  Dwarf_Addr pc __maybe_unused,
+                                                  Dwarf_Addr addr __maybe_unused,
+                                                  Dwarf_Die *die_mem __maybe_unused,
+                                                  int *offset __maybe_unused)
+{
+       return NULL;
+}
+
 #endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */
 
 #endif /* _DWARF_AUX_H */