From: German Gomez <hidden> Date: 2021-12-15 15:11:57
(This cset applies on top of [1])
Call-graphs on ARM64 using the option "--call-grah fp" are missing the
callers of the leaf functions. See [PATCH 6/6] for a before and after
after example using this cset.
[1] https://lore.kernel.org/all/20211207180653.1147374-1-german.gomez@arm.com/
---
Changes since v3
- Only record LR register instead of all registers in [PATCH 1/6].
- Introduce [PATCH 5/6] to refactor the SAMPL_REG macro.
- Fix compilation issues on different platforms.
Alexandre Truong (5):
perf tools: record ARM64 LR register automatically
perf tools: add a mechanism to inject stack frames
perf tools: Refactor script__setup_sample_type()
perf tools: enable dwarf_callchain_users on arm64
perf tools: determine if LR is the return address
German Gomez (1):
perf tools: Refactor SMPL_REG macro in perf_regs.h
tools/perf/arch/arm64/util/machine.c | 7 +++
tools/perf/builtin-record.c | 8 +++
tools/perf/builtin-report.c | 4 +-
tools/perf/builtin-script.c | 13 +---
tools/perf/util/Build | 1 +
.../util/arm64-frame-pointer-unwind-support.c | 63 +++++++++++++++++++
.../util/arm64-frame-pointer-unwind-support.h | 10 +++
tools/perf/util/callchain.c | 9 ++-
tools/perf/util/callchain.h | 4 +-
tools/perf/util/machine.c | 50 ++++++++++++++-
tools/perf/util/machine.h | 1 +
tools/perf/util/perf_regs.h | 7 ++-
12 files changed, 157 insertions(+), 20 deletions(-)
create mode 100644 tools/perf/util/arm64-frame-pointer-unwind-support.c
create mode 100644 tools/perf/util/arm64-frame-pointer-unwind-support.h
--
2.25.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: German Gomez <hidden> Date: 2021-12-15 15:12:06
From: Alexandre Truong <redacted>
On ARM64, automatically record the link register if the frame pointer
mode is on. It will be used to do a dwarf unwind to find the caller
of the leaf frame if the frame pointer was omitted.
Signed-off-by: Alexandre Truong <redacted>
Signed-off-by: German Gomez <redacted>
---
tools/perf/arch/arm64/util/machine.c | 7 +++++++
tools/perf/builtin-record.c | 8 ++++++++
tools/perf/util/callchain.h | 2 ++
3 files changed, 17 insertions(+)
@@ -5,6 +5,8 @@#include<string.h>#include"debug.h"#include"symbol.h"+#include"callchain.h"+#include"record.h"/* On arm64, kernel text segment starts at high memory address,*forexample0xffff00008xxxxxxx.Modulesstartatalowmemory
@@ -26,3 +28,8 @@ void arch__symbols__fixup_end(struct symbol *p, struct symbol *c)p->end=c->start;pr_debug4("%s sym:%s end:%#"PRIx64"\n",__func__,p->name,p->end);}++voidarch__add_leaf_frame_record_opts(structrecord_opts*opts)+{+opts->sample_user_regs|=sample_reg_masks[PERF_REG_ARM64_LR].mask;+}
From: German Gomez <hidden> Date: 2021-12-15 15:12:10
From: Alexandre Truong <redacted>
Add a mechanism for platforms to inject stack frames for the leaf
frame caller if there is enough information to determine a frame
is missing from dwarf or other post processing mechanisms.
Signed-off-by: Alexandre Truong <redacted>
Signed-off-by: German Gomez <redacted>
---
tools/perf/util/machine.c | 37 ++++++++++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
From: German Gomez <hidden> Date: 2021-12-15 15:12:20
From: Alexandre Truong <redacted>
On arm64, enable dwarf_callchain_users which will be needed
to do a dwarf unwind in order to get the caller of the leaf frame.
Signed-off-by: Alexandre Truong <redacted>
Signed-off-by: German Gomez <redacted>
---
tools/perf/builtin-report.c | 4 ++--
tools/perf/builtin-script.c | 4 ++--
tools/perf/util/callchain.c | 9 ++++++++-
tools/perf/util/callchain.h | 2 +-
4 files changed, 13 insertions(+), 6 deletions(-)
From: German Gomez <hidden> Date: 2021-12-15 15:12:25
Refactor the SAMPL_REG macro so that it can be used in a followup commit
to obtain the masks for ARM64 registers.
Signed-off-by: German Gomez <redacted>
---
tools/perf/util/perf_regs.h | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
From: German Gomez <hidden> Date: 2021-12-15 15:12:33
From: Alexandre Truong <redacted>
On arm64 and frame pointer mode (e.g: perf record --callgraph fp),
use dwarf unwind info to check if the link register is the return
address in order to inject it to the frame pointer stack.
Write the following application:
int a = 10;
void f2(void)
{
for (int i = 0; i < 1000000; i++)
a *= a;
}
void f1()
{
for (int i = 0; i < 10; i++)
f2();
}
int main(void)
{
f1();
return 0;
}
with the following compilation flags:
gcc -fno-omit-frame-pointer -fno-inline -O2
The compiler omits the frame pointer for f2 on arm. This is a problem
with any leaf call, for example an application with many different
calls to malloc() would always omit the calling frame, even if it
can be determined.
./perf record --call-graph fp ./a.out
./perf report
currently gives the following stack:
0xffffea52f361
_start
__libc_start_main
main
f2
After this change, perf report correctly shows f1() calling f2(),
even though it was missing from the frame pointer unwind:
./perf report
0xffffea52f361
_start
__libc_start_main
main
f1
f2
Signed-off-by: Alexandre Truong <redacted>
Signed-off-by: German Gomez <redacted>
---
tools/perf/util/Build | 1 +
.../util/arm64-frame-pointer-unwind-support.c | 63 +++++++++++++++++++
.../util/arm64-frame-pointer-unwind-support.h | 10 +++
tools/perf/util/machine.c | 19 ++++--
tools/perf/util/machine.h | 1 +
5 files changed, 89 insertions(+), 5 deletions(-)
create mode 100644 tools/perf/util/arm64-frame-pointer-unwind-support.c
create mode 100644 tools/perf/util/arm64-frame-pointer-unwind-support.h
From: Mark Rutland <mark.rutland@arm.com> Date: 2021-12-15 16:33:51
Hi,
On Wed, Dec 15, 2021 at 03:11:38PM +0000, German Gomez wrote:
From: Alexandre Truong <redacted>
On arm64 and frame pointer mode (e.g: perf record --callgraph fp),
use dwarf unwind info to check if the link register is the return
address in order to inject it to the frame pointer stack.
This series looks good overall, but as a general note the commit messages are a
bit hard to read because they jump into implementation details of the patch
(i.e. the change the patch makes) before explaining the problem (i.e. what the
patch is trying to solve).
It would be nice to have a short introduction, e.g.
When unwinding using frame pointers on arm64, the return address of the
current leaf function may be missed. The return address of a leaf function
may live in the LR and/or a frame record (and the location can change within
a function), so it is necessary to use DWARF to identify where to look for
the return address at any given point during a function.
For example:
unsigned long foo(unsigned long i)
{
i += 2;
i += 5;
}
... could be compiled as:
foo:
// return addr in LR
add x0, x0, #2
// return addr in LR
stp x29, x30, [SP, #-16]!
// return addr in LR
mov x29, sp
// return addr in LR *and* frame record
add x0, x0, #5
// return addr in LR *and* frame record
ldp x29, x30, [sp], #16
// return addr in LR
ret
Write the following application:
int a = 10;
void f2(void)
{
for (int i = 0; i < 1000000; i++)
a *= a;
}
void f1()
{
for (int i = 0; i < 10; i++)
f2();
}
int main(void)
{
f1();
return 0;
}
with the following compilation flags:
gcc -fno-omit-frame-pointer -fno-inline -O2
The compiler omits the frame pointer for f2 on arm. This is a problem
with any leaf call, for example an application with many different
calls to malloc() would always omit the calling frame, even if it
can be determined.
I think the wording here is slightly misleading. For f2, the compiler *doesn't
create a frame record*, but the frame pointer (to the caller's frame record)
remains and is not omitted.
Also, I think it's woth noting (as per the example I gave above) this applies
to *any* function which is the current leaf function, regardless of whether
that function creates a frame record at some point. For example, if `f1` is
interrupted before it creates its own frame record (or after it destroys the
frame record), the FP will point at the record created by `main` (containing
the caller of main), and `main` itself will be missing from the unwind as it
will only exist in the LR.
quoted hunk
./perf record --call-graph fp ./a.out
./perf report
currently gives the following stack:
0xffffea52f361
_start
__libc_start_main
main
f2
After this change, perf report correctly shows f1() calling f2(),
even though it was missing from the frame pointer unwind:
./perf report
0xffffea52f361
_start
__libc_start_main
main
f1
f2
Signed-off-by: Alexandre Truong <redacted>
Signed-off-by: German Gomez <redacted>
---
tools/perf/util/Build | 1 +
.../util/arm64-frame-pointer-unwind-support.c | 63 +++++++++++++++++++
.../util/arm64-frame-pointer-unwind-support.h | 10 +++
tools/perf/util/machine.c | 19 ++++--
tools/perf/util/machine.h | 1 +
5 files changed, 89 insertions(+), 5 deletions(-)
create mode 100644 tools/perf/util/arm64-frame-pointer-unwind-support.c
create mode 100644 tools/perf/util/arm64-frame-pointer-unwind-support.h
From: Mark Rutland <mark.rutland@arm.com> Date: 2021-12-15 16:37:55
On Wed, Dec 15, 2021 at 03:11:36PM +0000, German Gomez wrote:
quoted hunk
From: Alexandre Truong <redacted>
On arm64, enable dwarf_callchain_users which will be needed
to do a dwarf unwind in order to get the caller of the leaf frame.
Signed-off-by: Alexandre Truong <redacted>
Signed-off-by: German Gomez <redacted>
---
tools/perf/builtin-report.c | 4 ++--
tools/perf/builtin-script.c | 4 ++--
tools/perf/util/callchain.c | 9 ++++++++-
tools/perf/util/callchain.h | 2 +-
4 files changed, 13 insertions(+), 6 deletions(-)
I reckon it's worth mentioning *why* we need to do this; how about:
/*
* It's necessary to use libunwind to reliably determine the caller of
* a leaf function on aarch64, as otherwise we cannot know whether to
* start from the LR or FP.
*
* Always starting from the LR can result in duplicate or entirely
* erroneous entries. Always skipping the LR and starting from the FP
* can result in missing entries.
*/
Other than that, this looks fine to me!
Thanks,
Mark.
From: German Gomez <hidden> Date: 2021-12-17 11:57:27
Hi Mark,
Thanks for your review comments
On 15/12/2021 16:33, Mark Rutland wrote:
Hi,
On Wed, Dec 15, 2021 at 03:11:38PM +0000, German Gomez wrote:
quoted
From: Alexandre Truong <redacted>
On arm64 and frame pointer mode (e.g: perf record --callgraph fp),
use dwarf unwind info to check if the link register is the return
address in order to inject it to the frame pointer stack.
This series looks good overall, but as a general note the commit messages are a
bit hard to read because they jump into implementation details of the patch
(i.e. the change the patch makes) before explaining the problem (i.e. what the
patch is trying to solve).
It would be nice to have a short introduction, e.g.
Thanks for the suggestion! I'll run through the logs to see if I can
improve them.
When unwinding using frame pointers on arm64, the return address of the
current leaf function may be missed. The return address of a leaf function
may live in the LR and/or a frame record (and the location can change within
a function), so it is necessary to use DWARF to identify where to look for
the return address at any given point during a function.
For example:
unsigned long foo(unsigned long i)
{
i += 2;
i += 5;
}
... could be compiled as:
foo:
// return addr in LR
add x0, x0, #2
// return addr in LR
stp x29, x30, [SP, #-16]!
// return addr in LR
mov x29, sp
// return addr in LR *and* frame record
add x0, x0, #5
// return addr in LR *and* frame record
ldp x29, x30, [sp], #16
// return addr in LR
ret
quoted
Write the following application:
int a = 10;
void f2(void)
{
for (int i = 0; i < 1000000; i++)
a *= a;
}
void f1()
{
for (int i = 0; i < 10; i++)
f2();
}
int main(void)
{
f1();
return 0;
}
with the following compilation flags:
gcc -fno-omit-frame-pointer -fno-inline -O2
The compiler omits the frame pointer for f2 on arm. This is a problem
with any leaf call, for example an application with many different
calls to malloc() would always omit the calling frame, even if it
can be determined.
I think the wording here is slightly misleading. For f2, the compiler *doesn't
create a frame record*, but the frame pointer (to the caller's frame record)
remains and is not omitted.
Also, I think it's woth noting (as per the example I gave above) this applies
to *any* function which is the current leaf function, regardless of whether
that function creates a frame record at some point. For example, if `f1` is
interrupted before it creates its own frame record (or after it destroys the
frame record), the FP will point at the record created by `main` (containing
the caller of main), and `main` itself will be missing from the unwind as it
will only exist in the LR.
I see! I hadn't considered this. I guess it's not as likely to happen
but it's worth noting indeed.
quoted
./perf record --call-graph fp ./a.out
./perf report
currently gives the following stack:
0xffffea52f361
_start
__libc_start_main
main
f2
After this change, perf report correctly shows f1() calling f2(),
even though it was missing from the frame pointer unwind:
./perf report
0xffffea52f361
_start
__libc_start_main
main
f1
f2
Signed-off-by: Alexandre Truong <redacted>
Signed-off-by: German Gomez <redacted>
---
tools/perf/util/Build | 1 +
.../util/arm64-frame-pointer-unwind-support.c | 63 +++++++++++++++++++
.../util/arm64-frame-pointer-unwind-support.h | 10 +++
tools/perf/util/machine.c | 19 ++++--
tools/perf/util/machine.h | 1 +
5 files changed, 89 insertions(+), 5 deletions(-)
create mode 100644 tools/perf/util/arm64-frame-pointer-unwind-support.c
create mode 100644 tools/perf/util/arm64-frame-pointer-unwind-support.h
To prevent failures where? Is this something libunwind requires?
Admittedly I haven't look very deep into libunwind, but SP seems to go
ignored when getting the last 2 entries only, so here we set it to any
value.
Thanks,
German
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: German Gomez <hidden> Date: 2021-12-17 12:09:21
On 15/12/2021 16:37, Mark Rutland wrote:
On Wed, Dec 15, 2021 at 03:11:36PM +0000, German Gomez wrote:
quoted
[...]
+
+ /*
+ * It's possible to determine the caller of leaf frames with omitted
+ * frame pointers on aarch64 using libunwind, so enable it.
+ */
I reckon it's worth mentioning *why* we need to do this; how about:
/*
* It's necessary to use libunwind to reliably determine the caller of
* a leaf function on aarch64, as otherwise we cannot know whether to
* start from the LR or FP.
*
* Always starting from the LR can result in duplicate or entirely
* erroneous entries. Always skipping the LR and starting from the FP
* can result in missing entries.
*/
Other than that, this looks fine to me!
Thanks,
Mark.