Add a workaround for GCC -mfentry option. This enables perf
probe to find function parameters(arguments) at given function
entry point again.
With -mfentry, GCC (I've found this with Red Hat 4.7.0-5 and
Red Hat 4.7.2-2 on Fedora 17) generates odd debuginfo about
variable locations.
Here are examples:
-----
<1><9a58>: Abbrev Number: 86 (DW_TAG_subprogram)
<9a59> DW_AT_external : 1
<9a59> DW_AT_name : (indirect string, offset: 0xd82): unregister_di
e_notifier
<9a5d> DW_AT_decl_file : 1
<9a5e> DW_AT_decl_line : 551
<9a60> DW_AT_prototyped : 1
<9a60> DW_AT_type : <0x7c>
<9a64> DW_AT_low_pc : 0x740
<9a6c> DW_AT_high_pc : 0x75a
<9a74> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
<9a76> DW_AT_GNU_all_call_sites: 1
<9a76> DW_AT_sibling : <0x9aac>
<2><9a7a>: Abbrev Number: 87 (DW_TAG_formal_parameter)
<9a7b> DW_AT_name : nb
<9a7e> DW_AT_decl_file : 1
<9a7f> DW_AT_decl_line : 551
<9a81> DW_AT_type : <0x2a96>
<9a85> DW_AT_location : 0x172f (location list)
...
0000172f 0000000000000745 0000000000000750 (DW_OP_reg5 (rdi))
0000172f 0000000000000750 0000000000000757 (DW_OP_reg4 (rsi))
0000172f 0000000000000757 000000000000075a (DW_OP_GNU_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value)
0000172f <End of list>
-----
As you can see, the location of the parameter "nb", starts from 0x745
but unregister_die_notifier() function itself starts from 0x740.
-----
<1><9aac>: Abbrev Number: 86 (DW_TAG_subprogram)
<9aad> DW_AT_external : 1
<9aad> DW_AT_name : (indirect string, offset: 0x321f): notify_die
<9ab1> DW_AT_decl_file : 1
<9ab2> DW_AT_decl_line : 530
<9ab4> DW_AT_prototyped : 1
<9ab4> DW_AT_type : <0x7c>
<9ab8> DW_AT_low_pc : 0xb0
<9ac0> DW_AT_high_pc : 0xe0
<9ac8> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
<9aca> DW_AT_GNU_all_call_sites: 1
<9aca> DW_AT_sibling : <0x9b6c>
<2><9ace>: Abbrev Number: 87 (DW_TAG_formal_parameter)
<9acf> DW_AT_name : val
<9ad3> DW_AT_decl_file : 1
<9ad4> DW_AT_decl_line : 530
<9ad6> DW_AT_type : <0x2b36>
<9ada> DW_AT_location : 0x177b (location list)
...
0000177b 00000000000000b0 00000000000000cd (DW_OP_reg5 (rdi))
0000177b 00000000000000cd 00000000000000dd (DW_OP_reg4 (rsi))
0000177b 00000000000000dd 00000000000000e0 (DW_OP_GNU_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value)
0000177b <End of list>
-----
However, without fentry call (notifiy_die is marked notrace),
the parameter "val" location start with 0xb0, which is same as
notify_die start address.
To avoid failing variable search at the function entry point,
this add a workaround which adjusts var-search address +5
only if all the below conditions are true;
- given variable is a formal_parameter
- the searching address is a function entry point
- the formal_parameter has no location at the function entry point
Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Michal Marek <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: Steven Rostedt <[email protected]>
---
tools/perf/util/probe-finder.c | 40 +++++++++++++++++++++++++++++++++-------
1 file changed, 33 insertions(+), 7 deletions(-)
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 1daf5c1..84b3b5f 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -276,7 +276,7 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
* If tvar == NULL, this just checks variable can be converted.
*/
static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
- Dwarf_Op *fb_ops,
+ Dwarf_Op *fb_ops, bool fentry,
struct probe_trace_arg *tvar)
{
Dwarf_Attribute attr;
@@ -291,13 +291,31 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
goto static_var;
+ if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
+ return -ENOENT;
+
+ if (fentry && (dwarf_tag(vr_die) == DW_TAG_formal_parameter) &&
+ (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) == 0))
+ /*
+ * Workaround: GCC -mfentry option generates odd
+ * variable location entries which start from a
+ * function entry+5, even if it is a formal_parameter.
+ * On the other hand, for functions without fentry call
+ * (e.g. notrace function), the formal_parameter location
+ * starts from the function entry address.
+ * Here, if we find a formal_parameter which doesn't
+ * start from the function entry, but from function
+ * entry+5, it should be a buggy entry.
+ * We forcibly get the variable(parameter) location
+ * information from entry+5.
+ */
+ addr += 5;
+
/* TODO: handle more than 1 exprs */
- if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
- dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
- nops == 0) {
+ if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
+ nops == 0)
/* TODO: Support const_value */
return -ENOENT;
- }
if (op->atom == DW_OP_addr) {
static_var:
@@ -595,13 +613,19 @@ next:
static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
{
Dwarf_Die die_mem;
+ Dwarf_Addr eaddr;
+ bool fentry;
int ret;
pr_debug("Converting variable %s into trace event.\n",
dwarf_diename(vr_die));
+ /* GCC -mfentry workaround */
+ fentry = (dwarf_entrypc(&pf->sp_die, &eaddr) == 0) &&
+ (eaddr == pf->addr);
+
ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
- pf->tvar);
+ fentry, pf->tvar);
if (ret == -ENOENT)
pr_err("Failed to find the location of %s at this address.\n"
" Perhaps, it has been optimized out.\n", pf->pvar->var);
@@ -1220,7 +1244,9 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
if (tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) {
ret = convert_variable_location(die_mem, af->pf.addr,
- af->pf.fb_ops, NULL);
+ af->pf.fb_ops,
+ vl->point.offset == 0,
+ NULL);
if (ret == 0) {
ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
pr_debug2("Add new var: %s\n", buf);
On Wed, Oct 03, 2012 at 09:17:07PM +0900, Masami Hiramatsu wrote:
> Add a workaround for GCC -mfentry option. This enables perf
> probe to find function parameters(arguments) at given function
> entry point again.
Could you please file a gcc PR about this?
-Andi
(2012/10/03 21:27), Andi Kleen wrote:
> On Wed, Oct 03, 2012 at 09:17:07PM +0900, Masami Hiramatsu wrote:
>> Add a workaround for GCC -mfentry option. This enables perf
>> probe to find function parameters(arguments) at given function
>> entry point again.
>
> Could you please file a gcc PR about this?
>
OK, filed as PR54793 :)
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54793
Thank you,
--
Masami HIRAMATSU
IT Management Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]
On Wed, 2012-10-03 at 21:17 +0900, Masami Hiramatsu wrote:
> Here are examples:
> -----
> <1><9a58>: Abbrev Number: 86 (DW_TAG_subprogram)
> <9a59> DW_AT_external : 1
> <9a59> DW_AT_name : (indirect string, offset: 0xd82): unregister_di
> e_notifier
> <9a5d> DW_AT_decl_file : 1
> <9a5e> DW_AT_decl_line : 551
> <9a60> DW_AT_prototyped : 1
> <9a60> DW_AT_type : <0x7c>
> <9a64> DW_AT_low_pc : 0x740
> <9a6c> DW_AT_high_pc : 0x75a
> <9a74> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
> <9a76> DW_AT_GNU_all_call_sites: 1
> <9a76> DW_AT_sibling : <0x9aac>
> <2><9a7a>: Abbrev Number: 87 (DW_TAG_formal_parameter)
> <9a7b> DW_AT_name : nb
> <9a7e> DW_AT_decl_file : 1
> <9a7f> DW_AT_decl_line : 551
> <9a81> DW_AT_type : <0x2a96>
> <9a85> DW_AT_location : 0x172f (location list)
> ...
> 0000172f 0000000000000745 0000000000000750 (DW_OP_reg5 (rdi))
> 0000172f 0000000000000750 0000000000000757 (DW_OP_reg4 (rsi))
> 0000172f 0000000000000757 000000000000075a (DW_OP_GNU_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value)
> 0000172f <End of list>
> -----
>
> As you can see, the location of the parameter "nb", starts from 0x745
> but unregister_die_notifier() function itself starts from 0x740.
Um, no I can't see. I guess I need to go and read up on DWARF formats.
Any good recommended links?
A quick google gives me:
http://en.wikipedia.org/wiki/DWARF
http://wiki.dwarfstd.org/index.php?title=Dwarf_FAQ
http://www.eagercon.com/dwarf/dwarf-2.0.0.pdf
Is this what you recommend reading? Or is there better documentation?
-- Steve
On Wed, 2012-10-03 at 09:53 -0400, Steven Rostedt wrote:
> On Wed, 2012-10-03 at 21:17 +0900, Masami Hiramatsu wrote:
> > <1><9a58>: Abbrev Number: 86 (DW_TAG_subprogram)
> > <9a59> DW_AT_external : 1
> > <9a59> DW_AT_name : (indirect string, offset: 0xd82): unregister_di
> > e_notifier
> > <9a5d> DW_AT_decl_file : 1
> > <9a5e> DW_AT_decl_line : 551
> > <9a60> DW_AT_prototyped : 1
> > <9a60> DW_AT_type : <0x7c>
> > <9a64> DW_AT_low_pc : 0x740
> > <9a6c> DW_AT_high_pc : 0x75a
> > <9a74> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
> > <9a76> DW_AT_GNU_all_call_sites: 1
> > <9a76> DW_AT_sibling : <0x9aac>
> > <2><9a7a>: Abbrev Number: 87 (DW_TAG_formal_parameter)
> > <9a7b> DW_AT_name : nb
> > <9a7e> DW_AT_decl_file : 1
> > <9a7f> DW_AT_decl_line : 551
> > <9a81> DW_AT_type : <0x2a96>
> > <9a85> DW_AT_location : 0x172f (location list)
> > ...
> > 0000172f 0000000000000745 0000000000000750 (DW_OP_reg5 (rdi))
> > 0000172f 0000000000000750 0000000000000757 (DW_OP_reg4 (rsi))
> > 0000172f 0000000000000757 000000000000075a (DW_OP_GNU_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value)
> > 0000172f <End of list>
> > -----
> >
> > As you can see, the location of the parameter "nb", starts from 0x745
> > but unregister_die_notifier() function itself starts from 0x740.
>
> Um, no I can't see. I guess I need to go and read up on DWARF formats.
> Any good recommended links?
>
> A quick google gives me:
>
> http://en.wikipedia.org/wiki/DWARF
> http://wiki.dwarfstd.org/index.php?title=Dwarf_FAQ
> http://www.eagercon.com/dwarf/dwarf-2.0.0.pdf
>
> Is this what you recommend reading? Or is there better documentation?
DWARF2 is pretty old, the latest DWARF standard is
http://www.dwarfstd.org/Dwarf4Std.php
If you like details then you'll want to read section 2.5 DWARF
Expressions and 2.6 Location Descriptions. (That is what the
DW_AT_location attribute for the DW_TAG_formal_parameter points at.) You
might also want to read a bit about some of the DWARF5/GNU extensions
commonly used. I tried to add references to them on this page:
https://fedorahosted.org/elfutils/wiki/DwarfExtensions
(Look for the Expression Opcodes)
So in the above example it tries to say that for function (subprogram)
"unregister_di_notifier" runs from (low_pc) 0x740 till (high_pc) 0x75a
and for the parameter "nb" the value can be found in the rdi register
from address 0x745 till 0x750, in register rsi from address 0x750 till
0x575 and for 0x757 till 0x75a it would be the value of register rsi as
it was on function entry (yes, that just means it isn't available
anymore and you need to either unwind to function start to get it or
have it recorded on function entry to retrieve it at this point). The
above also implies that from address 0x740 till 0x745 GCC didn't record
the location of nb (or it just hasn't materialized yet).
Note that the GCC hackers already seem to have identified why that is in
this case: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54793#c2
Cheers,
Mark
(2012/10/04 1:00), Mark Wielaard wrote:
> On Wed, 2012-10-03 at 09:53 -0400, Steven Rostedt wrote:
>> On Wed, 2012-10-03 at 21:17 +0900, Masami Hiramatsu wrote:
>>> <1><9a58>: Abbrev Number: 86 (DW_TAG_subprogram)
>>> <9a59> DW_AT_external : 1
>>> <9a59> DW_AT_name : (indirect string, offset: 0xd82): unregister_di
>>> e_notifier
>>> <9a5d> DW_AT_decl_file : 1
>>> <9a5e> DW_AT_decl_line : 551
>>> <9a60> DW_AT_prototyped : 1
>>> <9a60> DW_AT_type : <0x7c>
>>> <9a64> DW_AT_low_pc : 0x740
>>> <9a6c> DW_AT_high_pc : 0x75a
>>> <9a74> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
>>> <9a76> DW_AT_GNU_all_call_sites: 1
>>> <9a76> DW_AT_sibling : <0x9aac>
>>> <2><9a7a>: Abbrev Number: 87 (DW_TAG_formal_parameter)
>>> <9a7b> DW_AT_name : nb
>>> <9a7e> DW_AT_decl_file : 1
>>> <9a7f> DW_AT_decl_line : 551
>>> <9a81> DW_AT_type : <0x2a96>
>>> <9a85> DW_AT_location : 0x172f (location list)
>>> ...
>>> 0000172f 0000000000000745 0000000000000750 (DW_OP_reg5 (rdi))
>>> 0000172f 0000000000000750 0000000000000757 (DW_OP_reg4 (rsi))
>>> 0000172f 0000000000000757 000000000000075a (DW_OP_GNU_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value)
>>> 0000172f <End of list>
>>> -----
>>>
>>> As you can see, the location of the parameter "nb", starts from 0x745
>>> but unregister_die_notifier() function itself starts from 0x740.
>>
>> Um, no I can't see. I guess I need to go and read up on DWARF formats.
>> Any good recommended links?
>>
>> A quick google gives me:
>>
>> http://en.wikipedia.org/wiki/DWARF
>> http://wiki.dwarfstd.org/index.php?title=Dwarf_FAQ
>> http://www.eagercon.com/dwarf/dwarf-2.0.0.pdf
>>
>> Is this what you recommend reading? Or is there better documentation?
>
> DWARF2 is pretty old, the latest DWARF standard is
> http://www.dwarfstd.org/Dwarf4Std.php
> If you like details then you'll want to read section 2.5 DWARF
> Expressions and 2.6 Location Descriptions. (That is what the
> DW_AT_location attribute for the DW_TAG_formal_parameter points at.) You
> might also want to read a bit about some of the DWARF5/GNU extensions
> commonly used. I tried to add references to them on this page:
> https://fedorahosted.org/elfutils/wiki/DwarfExtensions
> (Look for the Expression Opcodes)
>
> So in the above example it tries to say that for function (subprogram)
> "unregister_di_notifier" runs from (low_pc) 0x740 till (high_pc) 0x75a
> and for the parameter "nb" the value can be found in the rdi register
> from address 0x745 till 0x750, in register rsi from address 0x750 till
> 0x575 and for 0x757 till 0x75a it would be the value of register rsi as
> it was on function entry (yes, that just means it isn't available
> anymore and you need to either unwind to function start to get it or
> have it recorded on function entry to retrieve it at this point). The
> above also implies that from address 0x740 till 0x745 GCC didn't record
> the location of nb (or it just hasn't materialized yet).
>
> Note that the GCC hackers already seem to have identified why that is in
> this case: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54793#c2
Thanks for the perfect explanation!
And sorry, I should explain not why but what happened...
Without this patch, perf probe doesn't work correctly about finding
function parameters on function entries.
----
# ./perf probe -V vfs_read
Available variables at vfs_read
@<vfs_read+0>
(No matched variables)
----
Not only showing parameters, but also setting parameters failed.
----
# ./perf probe vfs_read buf
Failed to find the location of buf at this address.
Perhaps, it has been optimized out.
Failed to find 'buf' in this function.
Error: Failed to add events. (-2)
----
With this patch, perf can handle parameters again.
----
# ./perf probe -V vfs_read
Available variables at vfs_read
@<vfs_read+0>
char* buf
loff_t* pos
size_t count
struct file* file
----
and
----
# ./perf probe vfs_read buf
Added new event:
probe:vfs_read (on vfs_read with buf)
You can now use it in all perf tools, such as:
perf record -e probe:vfs_read -aR sleep 1
----
Thank you,
--
Masami HIRAMATSU
IT Management Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]