Message ID | 20231012035111.676789-29-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:51 -0700 Namhyung Kim <namhyung@kernel.org> wrote: > 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> Looks good to me. Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> BTW, for the global variable, you can also find it via maps. Can't you? Thanks, > --- > tools/perf/util/dwarf-aux.c | 80 +++++++++++++++++++++++++++++++++++++ > tools/perf/util/dwarf-aux.h | 14 +++++++ > 2 files changed, 94 insertions(+) > > diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c > index 5bb05c84d249..97d9ae56350e 100644 > --- a/tools/perf/util/dwarf-aux.c > +++ b/tools/perf/util/dwarf-aux.c > @@ -1266,8 +1266,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 */ > @@ -1328,6 +1332,82 @@ 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 > > /* > diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h > index 574405c57d3b..742098e3ee7e 100644 > --- a/tools/perf/util/dwarf-aux.h > +++ b/tools/perf/util/dwarf-aux.h > @@ -144,6 +144,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, > @@ -161,6 +166,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 */ > -- > 2.42.0.655.g421f12c284-goog > >
On Mon, Nov 6, 2023 at 7:25 AM Masami Hiramatsu <mhiramat@kernel.org> wrote: > > On Wed, 11 Oct 2023 20:50:51 -0700 > Namhyung Kim <namhyung@kernel.org> wrote: > > > 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> > > Looks good to me. > > Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> > > BTW, for the global variable, you can also find it via maps. Can't you? What do you mean by 'via maps'? The map in perf? Thanks, Namhyung
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 5bb05c84d249..97d9ae56350e 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -1266,8 +1266,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 */ @@ -1328,6 +1332,82 @@ 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 /* diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index 574405c57d3b..742098e3ee7e 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -144,6 +144,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, @@ -161,6 +166,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 */
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> --- tools/perf/util/dwarf-aux.c | 80 +++++++++++++++++++++++++++++++++++++ tools/perf/util/dwarf-aux.h | 14 +++++++ 2 files changed, 94 insertions(+)