Message ID | 20231012035111.676789-8-namhyung@kernel.org (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | perf tools: Introduce data type profiling (v1) | expand |
On Wed, 11 Oct 2023 20:50:30 -0700 Namhyung Kim <namhyung@kernel.org> wrote: > 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 simpliest and most common case, memory access usually happens > with a base register and an offset to the field so the register would > hold a pointer in a variable or function parameter. Then we can find > one if it has a location expression at the (instruction) address. So > this function only handles such a simple case for now. > > In this case, the expression would have 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 would reject expressions with more operations. > Nice! This looks good to me. Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Thank you, > Cc: Masami Hiramatsu <mhiramat@kernel.org> > Signed-off-by: Namhyung Kim <namhyung@kernel.org> > --- > tools/perf/util/dwarf-aux.c | 67 +++++++++++++++++++++++++++++++++++++ > tools/perf/util/dwarf-aux.h | 12 +++++++ > 2 files changed, 79 insertions(+) > > diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c > index 10aa32334d6f..652e6e7368a2 100644 > --- a/tools/perf/util/dwarf-aux.c > +++ b/tools/perf/util/dwarf-aux.c > @@ -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 > > /* > diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h > index f9d765f80fb0..b6f430730bd1 100644 > --- a/tools/perf/util/dwarf-aux.h > +++ b/tools/perf/util/dwarf-aux.h > @@ -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 */ > -- > 2.42.0.655.g421f12c284-goog >
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 10aa32334d6f..652e6e7368a2 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -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 /* diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index f9d765f80fb0..b6f430730bd1 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -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 */
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 simpliest and most common case, memory access usually happens with a base register and an offset to the field so the register would hold a pointer in a variable or function parameter. Then we can find one if it has a location expression at the (instruction) address. So this function only handles such a simple case for now. In this case, the expression would have 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 would reject expressions with more operations. Cc: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Namhyung Kim <namhyung@kernel.org> --- tools/perf/util/dwarf-aux.c | 67 +++++++++++++++++++++++++++++++++++++ tools/perf/util/dwarf-aux.h | 12 +++++++ 2 files changed, 79 insertions(+)