]> git.itanic.dy.fi Git - linux-stable/commitdiff
perf dwarf-aux: Add die_find_variable_by_reg() helper
authorNamhyung Kim <namhyung@kernel.org>
Thu, 9 Nov 2023 23:59:26 +0000 (15:59 -0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 10 Nov 2023 12:04:12 +0000 (09:04 -0300)
The die_find_variable_by_reg() will search for a variable or a parameter
sub-DIE in the given scope DIE where the location matches to the given
register.

For the simplest and most common case, memory access usually happens
with a base register and an offset to the field so the register holds a
pointer in a variable or function parameter.  Then we can find one if it
has a location expression at the (instruction) address.  This function
only handles such a simple case for now.

In this case, the expression has a DW_OP_regN operation where N < 32.
If the register index (N) is greater than or equal to 32, DW_OP_regx
operation with an operand which saves the value for the N would be used.
It rejects expressions with more operations.

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-8-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 10aa32334d6f276222f999ce26f612d031efcbef..652e6e7368a26fe67d27dac70704c6698e60c17b 100644 (file)
@@ -1245,6 +1245,73 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
 out:
        return ret;
 }
+
+/* Interval parameters for __die_find_var_reg_cb() */
+struct find_var_data {
+       /* Target instruction address */
+       Dwarf_Addr pc;
+       /* Target register */
+       unsigned reg;
+};
+
+/* Max number of registers DW_OP_regN supports */
+#define DWARF_OP_DIRECT_REGS  32
+
+/* Only checks direct child DIEs in the given scope. */
+static int __die_find_var_reg_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_Op *ops;
+       size_t nops;
+
+       if (tag != DW_TAG_variable && tag != DW_TAG_formal_parameter)
+               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) {
+               /* Assuming the location list is sorted by address */
+               if (end < data->pc)
+                       continue;
+               if (start > data->pc)
+                       break;
+
+               /* Only match with a simple case */
+               if (data->reg < DWARF_OP_DIRECT_REGS) {
+                       if (ops->atom == (DW_OP_reg0 + data->reg) && nops == 1)
+                               return DIE_FIND_CB_END;
+               } else {
+                       if (ops->atom == DW_OP_regx && ops->number == data->reg &&
+                           nops == 1)
+                               return DIE_FIND_CB_END;
+               }
+       }
+       return DIE_FIND_CB_SIBLING;
+}
+
+/**
+ * die_find_variable_by_reg - Find a variable saved in a register
+ * @sc_die: a scope DIE
+ * @pc: the program address to find
+ * @reg: the register number to find
+ * @die_mem: a buffer to save the resulting DIE
+ *
+ * Find the variable DIE accessed by the given register.
+ */
+Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
+                                   Dwarf_Die *die_mem)
+{
+       struct find_var_data data = {
+               .pc = pc,
+               .reg = reg,
+       };
+       return die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem);
+}
 #endif
 
 /*
index f9d765f80fb024332c4946d26272e5e5829a3da9..b6f430730bd13f19fea7234758005baa7489db84 100644 (file)
@@ -137,6 +137,10 @@ int die_get_scopes(Dwarf_Die *cu_die, Dwarf_Addr pc, Dwarf_Die **scopes);
 /* Get byte offset range of given variable DIE */
 int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf);
 
+/* Find a variable saved in the 'reg' at given address */
+Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
+                                   Dwarf_Die *die_mem);
+
 #else /*  HAVE_DWARF_GETLOCATIONS_SUPPORT */
 
 static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
@@ -146,6 +150,14 @@ static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
        return -ENOTSUP;
 }
 
+static inline Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die __maybe_unused,
+                                                 Dwarf_Addr pc __maybe_unused,
+                                                 int reg __maybe_unused,
+                                                 Dwarf_Die *die_mem __maybe_unused)
+{
+       return NULL;
+}
+
 #endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */
 
 #endif /* _DWARF_AUX_H */