perf tool: Haswell LBR call stack support (user)
v6 cover perf tool: Haswell LBR call stack support (user)
From: Kan Liang <[email protected]>
This is the user space patch for Haswell LBR call stack support. For many profiling tasks we need the callgraph. For example we often need to see the caller of a lock or the caller of a memcpy or other library function to actually tune the program. Frame pointer unwinding is efficient and works well. But frame pointers are off by default on 64bit code (and on modern 32bit gccs), so there are many binaries around that do not use frame pointers. Profiling unchanged production code is very useful in practice. On some CPUs frame pointer also has a high cost. Dwarf2 unwinding also does not always work and is extremely slow (upto 20% overhead).
Haswell has a new feature that utilizes the existing Last Branch Record facility to record call chains. When the feature is enabled, function call will be collected as normal, but as return instructions are executed the last captured branch record is popped from the on-chip LBR registers. The LBR call stack facility provides an alternative to get callgraph. It has some limitations too, but should work in most cases and is significantly faster than dwarf. Frame pointer unwinding is still the best default, but LBR call stack is a good alternative when nothing else works.
Please find the kernel part patch at https://lkml.org/lkml/2014/11/6/432
Changes since v1
- Update help document
- Force exclude_user to 0 with warning in LBR call stack
- Dump both lbr and fp info when report -D
- Reconstruct thread__resolve_callchain_sample and split it into two patches
- Use has_branch_callstack function to check LBR call stack available
Changes since v2
- Rebase to 025ce5d33373
Changes since v3
- Rebase to cc502c23aadf
- Separated function for lbr call stack sample resolve and print
- Some minor changes according to comments
Changes since V4
- Rebase to 09a6a1b
- Falling back to framepointers if LBR not availabe, and warning user
Changes since V5
- Only remove the 2/3 and part of 1/3 which have already been merged.
Kan Liang (2):
perf tools: enable LBR call stack support
perf tools: Construct LBR call chain
tools/perf/Documentation/perf-record.txt | 8 ++- tools/perf/builtin-record.c | 6 +- tools/perf/builtin-report.c | 2 + tools/perf/util/callchain.c | 8 +++ tools/perf/util/callchain.h | 1 + tools/perf/util/evsel.c | 21 ++++++- tools/perf/util/evsel.h | 4 ++ tools/perf/util/machine.c | 102 +++++++++++++++++++++++++++---- tools/perf/util/session.c | 64 +++++++++++++++++-- 9 files changed, 193 insertions(+), 23 deletions(-)
Signature · 1 line
1.8.3.2
PATCH v6 1/2 perf tools: enable LBR call stack support
From: Kan Liang <[email protected]>
Currently, there are two call chain recording options, fp and dwarf. Haswell has a new feature that utilizes the existing LBR facility to record call chains. So it provides the third options to record call chain. This patch enables the lbr call stack support.
LBR call stack has some limitations. It reuses current LBR facility, so LBR call stack and branch record can not be enabled at the same time. It is only available for user callchain.
However, LBR call stack can work on the user app which doesn't have frame-pointer or dwarf debug info compiled. It is a good alternative when nothing else works.
--- tools/perf/Documentation/perf-record.txt | 8 +++++++- tools/perf/builtin-record.c | 6 +++--- tools/perf/builtin-report.c | 2 ++ tools/perf/util/callchain.c | 8 ++++++++ tools/perf/util/callchain.h | 1 + tools/perf/util/evsel.c | 21 +++++++++++++++++++-- 6 files changed, 40 insertions(+), 6 deletions(-)
tools/perf/Documentation/perf-record.txt +7 -1
@@ -103,13 +103,19 @@ OPTIONS implies -g. Allows specifying "fp" (frame pointer) or "dwarf" - (DWARF's CFI - Call Frame Information) as the method to collect + (DWARF's CFI - Call Frame Information) or "lbr" + (Hardware Last Branch Record facility) as the method to collect the information used to show the call graphs. In some systems, where binaries are build with gcc --fomit-frame-pointer, using the "fp" method will produce bogus call graphs, using "dwarf", if available (perf tools linked to the libunwind library) should be used instead. + Using the "lbr" method doesn't require any compiler options. It + will produce call graphs from the hardware LBR registers. The + main limition is that it is only available on new Intel + platforms, such as Haswell. It can only get user call chain. It + doesn't work with branch stack sampling at the same time. -q:: --quiet::
tools/perf/builtin-record.c +3 -3
@@ -639,7 +639,7 @@ error:static void callchain_debug (void ) { -static const char *str[CALLCHAIN_MAX ] = {"NONE" ,"FP" ,"DWARF" }; +static const char *str[CALLCHAIN_MAX ] = {"NONE" ,"FP" ,"DWARF" ,"LBR" };pr_debug ("callchain: type %s\n" , str[callchain_param.record_mode]);
@@ -725,9 +725,9 @@ static struct record record = {#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: "#ifdef HAVE_DWARF_UNWIND_SUPPORT -const char record_callchain_help[] =CALLCHAIN_HELP "fp dwarf" ; +const char record_callchain_help[] =CALLCHAIN_HELP "fp dwarf lbr" ;#else -const char record_callchain_help[] =CALLCHAIN_HELP "fp" ; +const char record_callchain_help[] =CALLCHAIN_HELP "fp lbr" ;#endif /*
tools/perf/builtin-report.c +2 -0
@@ -262,6 +262,8 @@ static int report__setup_sample_type(struct report *rep)if ((sample_type &PERF_SAMPLE_REGS_USER ) && (sample_type &PERF_SAMPLE_STACK_USER )) callchain_param.record_mode =CALLCHAIN_DWARF ; +else if (sample_type &PERF_SAMPLE_BRANCH_STACK ) + callchain_param.record_mode =CALLCHAIN_LBR ;else callchain_param.record_mode =CALLCHAIN_FP ; }
tools/perf/util/callchain.c +8 -0
@@ -97,6 +97,14 @@ int parse_callchain_record_opt(const char *arg) callchain_param.dump_size = size; }#endif /* HAVE_DWARF_UNWIND_SUPPORT */ + }else if (!strncmp (name,"lbr" ,sizeof ("lbr" ))) { +if (!strtok_r (NULL ,"," , &saveptr)) { + callchain_param.record_mode =CALLCHAIN_LBR ; + ret =0 ; + }else +pr_err ("callchain: No more arguments " +"needed for --call-graph lbr\n" ); +break ; }else {pr_err ("callchain: Unknown --call-graph option " "value: %s\n" , arg);
tools/perf/util/callchain.h +1 -0
@@ -11,6 +11,7 @@ enum perf_call_graph_mode {CALLCHAIN_NONE ,CALLCHAIN_FP ,CALLCHAIN_DWARF , +CALLCHAIN_LBR , CALLCHAIN_MAX };
tools/perf/util/evsel.c +19 -2
@@ -537,13 +537,30 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) }static void -perf_evsel__config_callgraph (struct perf_evsel *evsel) +perf_evsel__config_callgraph (struct perf_evsel *evsel, +struct record_opts *opts) {bool function =perf_evsel__is_function_event (evsel);struct perf_event_attr *attr = &evsel->attr;perf_evsel__set_sample_bit (evsel,CALLCHAIN ); +if (callchain_param.record_mode ==CALLCHAIN_LBR ) { +if (!opts->branch_stack) { +if (attr->exclude_user) { +pr_warning ("LBR callstack option is only available " +"to get user callchain information. " +"Falling back to framepointers.\n" ); + }else { +perf_evsel__set_sample_bit (evsel,BRANCH_STACK ); + attr->branch_sample_type =PERF_SAMPLE_BRANCH_USER | +PERF_SAMPLE_BRANCH_CALL_STACK ; + } + }else +pr_warning ("Cannot use LBR callstack with branch stack. " +"Falling back to framepointers.\n" ); + } +if (callchain_param.record_mode ==CALLCHAIN_DWARF ) {if (!function) {perf_evsel__set_sample_bit (evsel,REGS_USER );
@@ -667,7 +684,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) evsel->attr.exclude_callchain_user =1 ;if (callchain_param.enabled && !evsel->no_aux_samples) -perf_evsel__config_callgraph (evsel); +perf_evsel__config_callgraph (evsel, opts);if (opts->sample_intr_regs) { attr->sample_regs_intr =PERF_REGS_MASK ;
Signature · 1 line
1.8.3.2
tip-bot for Kan Liang
merge bot[tip:perf/core] perf tools: Enable LBR call stack support
perf tools: Enable LBR call stack support
Commit-ID: aad2b21c151273fa7abc419dac51a980eff1dd17 Gitweb: http://git.kernel.org/tip/aad2b21c151273fa7abc419dac51a980eff1dd17 Author: Kan Liang <[email protected]> AuthorDate: Mon, 5 Jan 2015 13:23:04 -0500 Committer: Ingo Molnar <[email protected]> CommitDate: Wed, 18 Feb 2015 17:16:17 +0100
perf tools: Enable LBR call stack support
Currently, there are two call chain recording options, fp and dwarf.
Haswell has a new feature that utilizes the existing LBR facility to record call chains. Kernel side LBR support code provides this as a third option to record call chains. This patch enables the lbr call stack support on the tooling side.
LBR call stack has some limitations:
- It reuses current LBR facility, so LBR call stack and branch record can not be enabled at the same time.
- It is only available for user-space callchains.
However, it also offers some advantages:
- LBR call stack can work on user apps which don't have frame-pointers or dwarf debug info compiled. It is a good alternative when nothing else works.
--- tools/perf/Documentation/perf-record.txt | 8 +++++++- tools/perf/builtin-record.c | 6 +++--- tools/perf/builtin-report.c | 2 ++ tools/perf/util/callchain.c | 8 ++++++++ tools/perf/util/callchain.h | 1 + tools/perf/util/evsel.c | 21 +++++++++++++++++++-- 6 files changed, 40 insertions(+), 6 deletions(-)
tools/perf/Documentation/perf-record.txt +7 -1
@@ -115,13 +115,19 @@ OPTIONS implies -g. Allows specifying "fp" (frame pointer) or "dwarf" - (DWARF's CFI - Call Frame Information) as the method to collect + (DWARF's CFI - Call Frame Information) or "lbr" + (Hardware Last Branch Record facility) as the method to collect the information used to show the call graphs. In some systems, where binaries are build with gcc --fomit-frame-pointer, using the "fp" method will produce bogus call graphs, using "dwarf", if available (perf tools linked to the libunwind library) should be used instead. + Using the "lbr" method doesn't require any compiler options. It + will produce call graphs from the hardware LBR registers. The + main limition is that it is only available on new Intel + platforms, such as Haswell. It can only get user call chain. It + doesn't work with branch stack sampling at the same time. -q:: --quiet::
tools/perf/builtin-record.c +3 -3
@@ -658,7 +658,7 @@ error:static void callchain_debug (void ) { -static const char *str[CALLCHAIN_MAX ] = {"NONE" ,"FP" ,"DWARF" }; +static const char *str[CALLCHAIN_MAX ] = {"NONE" ,"FP" ,"DWARF" ,"LBR" };pr_debug ("callchain: type %s\n" , str[callchain_param.record_mode]);
@@ -751,9 +751,9 @@ static struct record record = {#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: "#ifdef HAVE_DWARF_UNWIND_SUPPORT -const char record_callchain_help[] =CALLCHAIN_HELP "fp dwarf" ; +const char record_callchain_help[] =CALLCHAIN_HELP "fp dwarf lbr" ;#else -const char record_callchain_help[] =CALLCHAIN_HELP "fp" ; +const char record_callchain_help[] =CALLCHAIN_HELP "fp lbr" ;#endif /*
tools/perf/builtin-report.c +2 -0
@@ -249,6 +249,8 @@ static int report__setup_sample_type(struct report *rep)if ((sample_type &PERF_SAMPLE_REGS_USER ) && (sample_type &PERF_SAMPLE_STACK_USER )) callchain_param.record_mode =CALLCHAIN_DWARF ; +else if (sample_type &PERF_SAMPLE_BRANCH_STACK ) + callchain_param.record_mode =CALLCHAIN_LBR ;else callchain_param.record_mode =CALLCHAIN_FP ; }
tools/perf/util/callchain.c +8 -0
@@ -97,6 +97,14 @@ int parse_callchain_record_opt(const char *arg) callchain_param.dump_size = size; }#endif /* HAVE_DWARF_UNWIND_SUPPORT */ + }else if (!strncmp (name,"lbr" ,sizeof ("lbr" ))) { +if (!strtok_r (NULL ,"," , &saveptr)) { + callchain_param.record_mode =CALLCHAIN_LBR ; + ret =0 ; + }else +pr_err ("callchain: No more arguments " +"needed for --call-graph lbr\n" ); +break ; }else {pr_err ("callchain: Unknown --call-graph option " "value: %s\n" , arg);
tools/perf/util/callchain.h +1 -0
@@ -11,6 +11,7 @@ enum perf_call_graph_mode {CALLCHAIN_NONE ,CALLCHAIN_FP ,CALLCHAIN_DWARF , +CALLCHAIN_LBR , CALLCHAIN_MAX };
tools/perf/util/evsel.c +19 -2
@@ -537,13 +537,30 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) }static void -perf_evsel__config_callgraph (struct perf_evsel *evsel) +perf_evsel__config_callgraph (struct perf_evsel *evsel, +struct record_opts *opts) {bool function =perf_evsel__is_function_event (evsel);struct perf_event_attr *attr = &evsel->attr;perf_evsel__set_sample_bit (evsel,CALLCHAIN ); +if (callchain_param.record_mode ==CALLCHAIN_LBR ) { +if (!opts->branch_stack) { +if (attr->exclude_user) { +pr_warning ("LBR callstack option is only available " +"to get user callchain information. " +"Falling back to framepointers.\n" ); + }else { +perf_evsel__set_sample_bit (evsel,BRANCH_STACK ); + attr->branch_sample_type =PERF_SAMPLE_BRANCH_USER | +PERF_SAMPLE_BRANCH_CALL_STACK ; + } + }else +pr_warning ("Cannot use LBR callstack with branch stack. " +"Falling back to framepointers.\n" ); + } +if (callchain_param.record_mode ==CALLCHAIN_DWARF ) {if (!function) {perf_evsel__set_sample_bit (evsel,REGS_USER );
@@ -667,7 +684,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) evsel->attr.exclude_callchain_user =1 ;if (callchain_param.enabled && !evsel->no_aux_samples) -perf_evsel__config_callgraph (evsel); +perf_evsel__config_callgraph (evsel, opts);if (opts->sample_intr_regs) { attr->sample_regs_intr =PERF_REGS_MASK ;
PATCH v6 2/2 perf tools: Construct LBR call chain
In reply to [email protected]From: Kan Liang <[email protected]>
LBR call stack only has user callchain. It is output as PERF_SAMPLE_BRANCH_STACK data format. For the kernel callchain, it's still from PERF_SAMPLE_CALLCHAIN.
The perf tool has to handle both data sources to construct a complete callstack.
For perf report -D option, both lbr and fp information will be displayed.
A new call chain recording option "lbr" is introduced into perf tool for LBR call stack. The user can use --call-graph lbr to get the call stack information from hardware.
Here are some examples.
When profiling bc(1) on Fedora 19:
echo 'scale=2000; 4*a(1)' > cmd; perf record --call-graph lbr bc -l < cmd
If enabling LBR, perf report output looks like:
50.36% bc bc [.] bc_divide
|
--- bc_divide
execute
run_code
yyparse
main
__libc_start_main
_start
33.66% bc bc [.] _one_mult
|
--- _one_mult
bc_divide
execute
run_code
yyparse
main
__libc_start_main
_start
7.62% bc bc [.] _bc_do_add
|
--- _bc_do_add
|
|--99.89%-- 0x2000186a8
--0.11%-- [...]
6.83% bc bc [.] _bc_do_sub
|
--- _bc_do_sub
|
|--99.94%-- bc_add
| execute
| run_code
| yyparse
| main
| __libc_start_main
| _start
--0.06%-- [...]
0.46% bc libc-2.17.so [.] __memset_sse2
|
--- __memset_sse2
|
|--54.13%-- bc_new_num
| |
| |--51.00%-- bc_divide
| | execute
| | run_code
| | yyparse
| | main
| | __libc_start_main
| | _start
| |
| |--30.46%-- _bc_do_sub
| | bc_add
| | execute
| | run_code
| | yyparse
| | main
| | __libc_start_main
| | _start
| |
| --18.55%-- _bc_do_add
| bc_add
| execute
| run_code
| yyparse
| main
| __libc_start_main
| _start
|
--45.87%-- bc_divide
execute
run_code
yyparse
main
__libc_start_main
_start
If using FP, perf report output looks like:
echo 'scale=2000; 4*a(1)' > cmd; perf record --call-graph fp bc -l < cmd
50.49% bc bc [.] bc_divide
|
--- bc_divide
33.57% bc bc [.] _one_mult
|
--- _one_mult
7.61% bc bc [.] _bc_do_add
|
--- _bc_do_add
0x2000186a8
6.88% bc bc [.] _bc_do_sub
|
--- _bc_do_sub
0.42% bc libc-2.17.so [.] __memcpy_ssse3_back
|
--- __memcpy_ssse3_backIf using LBR, perf report -D output looks like:
3458145275743 0x2fd750 [0xd8]: PERF_RECORD_SAMPLE(IP, 0x2): 9748/9748:0x408ea8 period: 609644 addr: 0
... LBR call chain: nr:8
..... 0: fffffffffffffe00 ..... 1: 0000000000408e50 ..... 2: 000000000040a458 ..... 3: 000000000040562e ..... 4: 0000000000408590 ..... 5: 00000000004022c0 ..... 6: 00000000004015dd ..... 7: 0000003d1cc21b43
... FP chain: nr:2
..... 0: fffffffffffffe00
..... 1: 0000000000408ea8
... thread: bc:9748
...... dso: /usr/bin/bc
The LBR call stack has following known limitations
- Zero length calls are not filtered out by hardware
- Exception handing such as setjmp/longjmp will have calls/returns not match
- Pushing different return address onto the stack will have calls/returns
not match
- If callstack is deeper than the LBR, only the last entries are captured
--- tools/perf/util/evsel.h | 4 ++ tools/perf/util/machine.c | 102 +++++++++++++++++++++++++++++++++++++++++----- tools/perf/util/session.c | 64 ++++++++++++++++++++++++++--- 3 files changed, 153 insertions(+), 17 deletions(-)
tools/perf/util/evsel.h +4 -0
@@ -355,4 +355,8 @@ for ((_evsel) = list_entry((_leader)->node.next, struct perf_evsel, node); \ (_evsel ) && (_evsel)->leader == (_leader); \ (_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node)) +static inline bool has_branch_callstack (struct perf_evsel *evsel) +{ +return evsel->attr.branch_sample_type &PERF_SAMPLE_BRANCH_CALL_STACK ; +}#endif /* __PERF_EVSEL_H */
tools/perf/util/machine.c +91 -11
126 lines not on this page · raw message
tools/perf/util/session.c +58 -6
82 lines not on this page · raw message
Signature · 1 line
1.8.3.2
tip-bot for Kan Liang
merge bot[tip:perf/core] perf tools: Construct LBR call chain
perf tools: Construct LBR call chain
Commit-ID: 384b60557b5522fcb99646f0eb6e7a344cdb94c6 Gitweb: http://git.kernel.org/tip/384b60557b5522fcb99646f0eb6e7a344cdb94c6 Author: Kan Liang <[email protected]> AuthorDate: Mon, 5 Jan 2015 13:23:05 -0500 Committer: Ingo Molnar <[email protected]> CommitDate: Wed, 18 Feb 2015 17:16:18 +0100
perf tools: Construct LBR call chain
LBR call stack only has user-space callchains. It is output in the PERF_SAMPLE_BRANCH_STACK data format. For kernel callchains, it's still in the form of PERF_SAMPLE_CALLCHAIN.
The perf tool has to handle both data sources to construct a complete callstack.
For the "perf report -D" option, both lbr and fp information will be displayed.
A new call chain recording option "lbr" is introduced into the perf tool for LBR call stack. The user can use --call-graph lbr to get the call stack information from hardware.
Here are some examples.
When profiling bc(1) on Fedora 19:
echo 'scale=2000; 4*a(1)' > cmd; perf record --call-graph lbr bc -l < cmd
If enabling LBR, perf report output looks like:
50.36% bc bc [.] bc_divide
|
--- bc_divide
execute
run_code
yyparse
main
__libc_start_main
_start
33.66% bc bc [.] _one_mult
|
--- _one_mult
bc_divide
execute
run_code
yyparse
main
__libc_start_main
_start
7.62% bc bc [.] _bc_do_add
|
--- _bc_do_add
|
|--99.89%-- 0x2000186a8
--0.11%-- [...]
6.83% bc bc [.] _bc_do_sub
|
--- _bc_do_sub
|
|--99.94%-- bc_add
| execute
| run_code
| yyparse
| main
| __libc_start_main
| _start
--0.06%-- [...]
0.46% bc libc-2.17.so [.] __memset_sse2
|
--- __memset_sse2
|
|--54.13%-- bc_new_num
| |
| |--51.00%-- bc_divide
| | execute
| | run_code
| | yyparse
| | main
| | __libc_start_main
| | _start
| |
| |--30.46%-- _bc_do_sub
| | bc_add
| | execute
| | run_code
| | yyparse
| | main
| | __libc_start_main
| | _start
| |
| --18.55%-- _bc_do_add
| bc_add
| execute
| run_code
| yyparse
| main
| __libc_start_main
| _start
|
--45.87%-- bc_divide
execute
run_code
yyparse
main
__libc_start_main
_startIf using FP, perf report output looks like:
echo 'scale=2000; 4*a(1)' > cmd; perf record --call-graph fp bc -l < cmd
50.49% bc bc [.] bc_divide
|
--- bc_divide
33.57% bc bc [.] _one_mult
|
--- _one_mult
7.61% bc bc [.] _bc_do_add
|
--- _bc_do_add
0x2000186a8
6.88% bc bc [.] _bc_do_sub
|
--- _bc_do_sub
0.42% bc libc-2.17.so [.] __memcpy_ssse3_back
|
--- __memcpy_ssse3_backIf using LBR, perf report -D output looks like:
3458145275743 0x2fd750 [0xd8]: PERF_RECORD_SAMPLE(IP, 0x2): 9748/9748: 0x408ea8 period: 609644 addr: 0 ... LBR call chain: nr:8
..... 0: fffffffffffffe00 ..... 1: 0000000000408e50 ..... 2: 000000000040a458 ..... 3: 000000000040562e ..... 4: 0000000000408590 ..... 5: 00000000004022c0 ..... 6: 00000000004015dd ..... 7: 0000003d1cc21b43
... FP chain: nr:2
..... 0: fffffffffffffe00
..... 1: 0000000000408ea8
... thread: bc:9748
...... dso: /usr/bin/bc
The LBR call stack has the following known limitations:
- Zero length calls are not filtered out by the hardware
- Exception handing such as setjmp/longjmp will have calls/returns not match
- Pushing different return address onto the stack will have calls/returns not match
- If callstack is deeper than the LBR, only the last entries are captured
--- tools/perf/util/evsel.h | 4 ++ tools/perf/util/machine.c | 102 +++++++++++++++++++++++++++++++++++++++++----- tools/perf/util/session.c | 64 ++++++++++++++++++++++++++--- 3 files changed, 153 insertions(+), 17 deletions(-)
tools/perf/util/evsel.h +4 -0
@@ -355,4 +355,8 @@ for ((_evsel) = list_entry((_leader)->node.next, struct perf_evsel, node); \ (_evsel ) && (_evsel)->leader == (_leader); \ (_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node)) +static inline bool has_branch_callstack (struct perf_evsel *evsel) +{ +return evsel->attr.branch_sample_type &PERF_SAMPLE_BRANCH_CALL_STACK ; +}#endif /* __PERF_EVSEL_H */
tools/perf/util/machine.c +91 -11
126 lines not on this page · raw message
tools/perf/util/session.c +58 -6
82 lines not on this page · raw message
Peter Zijlstra
maintainerThanks, stuck these in with the kernel patches.