Skip to contentSkip to conversation

perf tool: Haswell LBR call stack support (user)

PATCH Superseded [email protected] 3 participants 9 messages Nov 18, 2014 – Nov 20, 2014 perf events

v4 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v4 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.

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

Kan Liang (3):
perf tools: enable LBR call stack support
perf tool: Move cpumode resolve code to add_callchain_ip 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              |  10 +-
 tools/perf/util/callchain.h              |   1 +
 tools/perf/util/evsel.c                  |  21 +++-
 tools/perf/util/evsel.h                  |   4 +
 tools/perf/util/machine.c                | 174 ++++++++++++++++++++++---------
 tools/perf/util/session.c                |  64 ++++++++++--
 9 files changed, 229 insertions(+), 61 deletions(-)
Signature · 1 line
1.8.3.2
#1 lore raw

PATCH v4 1/3 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              | 10 +++++++++-
 tools/perf/util/callchain.h              |  1 +
 tools/perf/util/evsel.c                  | 21 +++++++++++++++++++--
 6 files changed, 41 insertions(+), 7 deletions(-)
tools/perf/Documentation/perf-record.txt +7 -1
index 398f8d5..03d9939 100644
@@ -100,13 +100,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
index 582c4da..e486627 100644
@@ -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
index fb272ff..ebbf3f7 100644
@@ -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 +9 -1
index cf524a3..64c8913 100644
@@ -77,7 +77,7 @@ int parse_callchain_record_opt(const char *arg)
 				ret = 0;
 			} else
 				pr_err("callchain: No more arguments "
-				       "needed for -g fp\n");
+				       "needed for --call-graph fp\n");
 			break;
 
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
@@ -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
index dbc08cf..b4b61d1 100644
@@ -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
index 12b4396..7cbe2e9 100644
@@ -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) {
+			perf_evsel__set_sample_bit(evsel, BRANCH_STACK);
+			attr->branch_sample_type = PERF_SAMPLE_BRANCH_USER |
+						PERF_SAMPLE_BRANCH_CALL_STACK;
+			if (attr->exclude_user) {
+				attr->exclude_user = 0;
+
+				pr_warning("LBR callstack option is only available"
+					   " to get user callchain information."
+					   " Force exclude_user to 0.\n");
+			}
+		} else
+			 pr_info("Cannot use LBR callstack with branch stack\n");
+	}
+
 	if (callchain_param.record_mode == CALLCHAIN_DWARF) {
 		if (!function) {
 			perf_evsel__set_sample_bit(evsel, REGS_USER);
@@ -659,7 +676,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
 	}
 
 	if (callchain_param.enabled && !evsel->no_aux_samples)
-		perf_evsel__config_callgraph(evsel);
+		perf_evsel__config_callgraph(evsel, opts);
 
 	if (target__has_cpu(&opts->target))
 		perf_evsel__set_sample_bit(evsel, CPU);
Signature · 1 line
1.8.3.2
maintainer
#2 lore raw

Re: [PATCH V4 1/3] perf tools: enable LBR call stack support

Quoted 13 lines · kan liang

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.

[SNIP]

Quoted 12 lines

static void

tools/perf/util/evsel.c +4 -1
-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) {

tools/perf/util/evsel.c int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) · line 559

Quoted 10 lines
tools/perf/util/evsel.c +10 -0
+			perf_evsel__set_sample_bit(evsel, BRANCH_STACK);
+			attr->branch_sample_type = PERF_SAMPLE_BRANCH_USER |
+						PERF_SAMPLE_BRANCH_CALL_STACK;
+			if (attr->exclude_user) {
+				attr->exclude_user = 0;
+
+				pr_warning("LBR callstack option is only available"
+					   " to get user callchain information."
+					   " Force exclude_user to 0.\n");
+			}

I'm not sure what's in a higher priority - maybe I missed earlier discussion. IOW what about this?

			if (attr->exclude_user) {
				pr_warning("LBR callstack option is only available"
					   " to get user callchain information.\n");
			} else {
				perf_evsel__set_sample_bit(evsel, BRANCH_STACK);
				attr->branch_sample_type = PERF_SAMPLE_BRANCH_USER |
							PERF_SAMPLE_BRANCH_CALL_STACK;
			}

tools/perf/util/evsel.c int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) · line 561

Quoted 2 lines
tools/perf/util/evsel.c +2 -0
+		} else
+			 pr_info("Cannot use LBR callstack with branch stack\n");

It seems pr_warning is more appropriate here.

Thanks,
Namhyung

Quoted 14 lines
tools/perf/util/evsel.c +3 -1
+	}
+
 	if (callchain_param.record_mode == CALLCHAIN_DWARF) {
 		if (!function) {
 			perf_evsel__set_sample_bit(evsel, REGS_USER);
@@ -659,7 +676,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
 	}
 
 	if (callchain_param.enabled && !evsel->no_aux_samples)
-		perf_evsel__config_callgraph(evsel);
+		perf_evsel__config_callgraph(evsel, opts);
 
 	if (target__has_cpu(&opts->target))
 		perf_evsel__set_sample_bit(evsel, CPU);
author
#3 lore raw

RE: [PATCH V4 1/3] perf tools: enable LBR call stack support

Quoted 50 lines · kan liang
Quoted 13 lines

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.

[SNIP]

tools/perf/util/evsel.c int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)

Quoted 13 lines

static void

tools/perf/util/evsel.c +5 -1
-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) {
+			perf_evsel__set_sample_bit(evsel,

BRANCH_STACK);

Review

Quoted 1 line
(quoted diff) +1 -0
+			attr->branch_sample_type =

PERF_SAMPLE_BRANCH_USER |

Quoted 1 line

+

	PERF_SAMPLE_BRANCH_CALL_STACK;

tools/perf/util/evsel.c int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)

Quoted 4 lines
tools/perf/util/evsel.c +4 -0
+			if (attr->exclude_user) {
+				attr->exclude_user = 0;
+
+				pr_warning("LBR callstack option is only

available"

Review

Quoted 1 line
(quoted diff) +1 -0
+					   " to get user callchain

information."

Review

Quoted 2 lines
(quoted diff) +2 -0
+					   " Force exclude_user to 0.\n");
+			}

I'm not sure what's in a higher priority - maybe I missed earlier discussion. IOW what about this?

Quoted 5 lines
			if (attr->exclude_user) {
				pr_warning("LBR callstack option is only

available"
" to get user callchain
information.\n");

I think either is fine. I'd like to add more info "Falling back to framepointers." based on your changes.
So the user know what they will get then.

What do you think?

pr_warning("LBR callstack option is only available"

	         " to get user callchain information."
	          " Falling back to framepointers.\n");

pr_ warning ("Cannot use LBR callstack with branch stack"

                " Falling back to framepointers.\n");

Thanks,
Kan

Quoted 35 lines
			} else {
				perf_evsel__set_sample_bit(evsel,
BRANCH_STACK);
				attr->branch_sample_type =

PERF_SAMPLE_BRANCH_USER |

	PERF_SAMPLE_BRANCH_CALL_STACK;
			}

Review

Quoted 2 lines
(quoted diff) +2 -0
+		} else
+			 pr_info("Cannot use LBR callstack with branch

stack\n");

It seems pr_warning is more appropriate here.

Thanks,
Namhyung

Quoted 5 lines
tools/perf/util/evsel.c +2 -0
+	}
+
 	if (callchain_param.record_mode == CALLCHAIN_DWARF) {
 		if (!function) {
 			perf_evsel__set_sample_bit(evsel, REGS_USER);
(quoted diff) +0 -0
@@ -659,7 +676,7 @@
Quoted 1 line

void perf_evsel__config(struct perf_evsel *evsel, struct record_opts

*opts)

Quoted 8 lines
tools/perf/util/evsel.c +1 -1
 	}
 
 	if (callchain_param.enabled && !evsel->no_aux_samples)
-		perf_evsel__config_callgraph(evsel);
+		perf_evsel__config_callgraph(evsel, opts);
 
 	if (target__has_cpu(&opts->target))
 		perf_evsel__set_sample_bit(evsel, CPU);
maintainer
#4 lore raw

Re: [PATCH V4 1/3] perf tools: enable LBR call stack support

Quoted 16 lines · Kan Liang
Quoted 12 lines · kan liang

tools/perf/util/evsel.c int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) · line 559

Quoted 7 lines
tools/perf/util/evsel.c +7 -0
+			if (attr->exclude_user) {
+				attr->exclude_user = 0;
+
+				pr_warning("LBR callstack option is only available"
+					   " to get user callchain information."
+					   " Force exclude_user to 0.\n");
+			}

I'm not sure what's in a higher priority - maybe I missed earlier discussion. IOW what about this?

Quoted 3 lines
			if (attr->exclude_user) {
				pr_warning("LBR callstack option is only available"
					   " to get user callchain information.\n");
Quoted 5 lines

I think either is fine. I'd like to add more info "Falling back to framepointers." based on your changes.
So the user know what they will get then.

What do you think?

Looks good to me. But I still slightly prefer not to override user settings. But it's not a strong opinion though - I'd like to here from others.

Thanks,
Namhyung

Quoted 7 lines

pr_warning("LBR callstack option is only available"

	         " to get user callchain information."
	          " Falling back to framepointers.\n");

pr_ warning ("Cannot use LBR callstack with branch stack"

                " Falling back to framepointers.\n");
#5 lore raw

Re: [PATCH V4 1/3] perf tools: enable LBR call stack support

Quoted 22 lines · Namhyung Kim
Quoted 20 lines · Kan Liang
Quoted 9 lines · kan liang

tools/perf/util/evsel.c int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) · line 559

Quoted 7 lines
tools/perf/util/evsel.c +7 -0
+                  if (attr->exclude_user) {
+                          attr->exclude_user = 0;
+
+                          pr_warning("LBR callstack option is only available"
+                                     " to get user callchain information."
+                                     " Force exclude_user to 0.\n");
+                  }
Quoted 6 lines

I'm not sure what's in a higher priority - maybe I missed earlier discussion. IOW what about this?

                     if (attr->exclude_user) {
                             pr_warning("LBR callstack option is only available"
                                        " to get user callchain information.\n");

I think either is fine. I'd like to add more info "Falling back to framepointers." based on your changes.
So the user know what they will get then.

Quoted 6 lines
Quoted 1 line

What do you think?

Looks good to me. But I still slightly prefer not to override user settings. But it's not a strong opinion though - I'd like to here from others.

I don't like when the tool changes the use settings under the hood. I think perf did that with cycles -> TASK_CLOCK if PMU was not supported and that was very confusing to me especially with no warning.
So if LBR Call stack mode is not avail, then inform the user with a warning or error, don't swap silently.

author
#6 lore raw

RE: [PATCH V4 1/3] perf tools: enable LBR call stack support

Quoted 36 lines
Quoted 11 lines · Namhyung Kim
Quoted 9 lines · Kan Liang
Quoted 9 lines · kan liang
Quoted 7 lines
tools/perf/util/evsel.c +7 -0
+                  if (attr->exclude_user) {
+                          attr->exclude_user = 0;
+
+                          pr_warning("LBR callstack option is only available"
+                                     " to get user callchain information."
+                                     " Force exclude_user to 0.\n");
+                  }
Quoted 1 line
Quoted 1 line
Quoted 1 line

I'm not sure what's in a higher priority - maybe I missed earlier

discussion.

Quoted 8 lines
Quoted 8 lines
Quoted 6 lines

IOW what about this?

if (attr->exclude_user) {
pr_warning("LBR callstack option is only available"
" to get user callchain
information.\n");

I think either is fine. I'd like to add more info "Falling back to

framepointers."

Quoted 9 lines
Quoted 4 lines

based on your changes.
So the user know what they will get then.

What do you think?

Looks good to me. But I still slightly prefer not to override user settings. But it's not a strong opinion though - I'd like to here from others.

Quoted 5 lines

I don't like when the tool changes the use settings under the hood. I think perf did that with cycles -> TASK_CLOCK if PMU was not supported and that was very confusing to me especially with no warning.
So if LBR Call stack mode is not avail, then inform the user with a warning or error, don't swap silently.

OK. So the new patch will warn the user if LBR call stack is no available. It will also tell the user that the FP mode will replace the LBR mode.

Thanks,
Kan ���{.n�+�������+%�����ݶ��w��{.n�+����{��G�����{ay�ʇڙ�,j��f���h��������z_�(�階�ݢj"���m������G������?����&���~��iO��z��v�^�m���� ������?�I�

#7 lore raw

PATCH v4 2/3 perf tool: Move cpumode resolve code to add_callchain_ip

In reply to [email protected]

From: Kan Liang <[email protected]>

Using flag to distinguish between branch_history and normal callchain. Move the cpumode to add_callchain_ip function.
No change in behavior.

---
 tools/perf/util/machine.c | 72 +++++++++++++++++++++++------------------------
 1 file changed, 35 insertions(+), 37 deletions(-)
tools/perf/util/machine.c +35 -37
index 5c4d7f8..7fd3cf2 100644
@@ -1385,19 +1385,46 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
 static int add_callchain_ip(struct thread *thread,
 			    struct symbol **parent,
 			    struct addr_location *root_al,
-			    int cpumode,
+			    bool branch_history,
 			    u64 ip)
 {
 	struct addr_location al;
 
 	al.filtered = 0;
 	al.sym = NULL;
-	if (cpumode == -1)
+	if (branch_history)
 		thread__find_cpumode_addr_location(thread, MAP__FUNCTION,
 						   ip, &al);
-	else
+	else {
+		u8 cpumode = PERF_RECORD_MISC_USER;
+
+		if (ip >= PERF_CONTEXT_MAX) {
+			switch (ip) {
+			case PERF_CONTEXT_HV:
+				cpumode = PERF_RECORD_MISC_HYPERVISOR;
+				break;
+			case PERF_CONTEXT_KERNEL:
+				cpumode = PERF_RECORD_MISC_KERNEL;
+				break;
+			case PERF_CONTEXT_USER:
+				cpumode = PERF_RECORD_MISC_USER;
+				break;
+			default:
+				pr_debug("invalid callchain context: "
+					 "%"PRId64"\n", (s64) ip);
+				/*
+				 * It seems the callchain is corrupted.
+				 * Discard all.
+				 */
+				callchain_cursor_reset(&callchain_cursor);
+				return 1;
+			}
+			return 0;
+		}
 		thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
 				   ip, &al);
+	}
+
 	if (al.sym != NULL) {
 		if (sort__has_parent && !*parent &&
 		    symbol__match_regex(al.sym, &parent_regex))
@@ -1480,11 +1507,8 @@ static int thread__resolve_callchain_sample(struct thread *thread,
 					     struct addr_location *root_al,
 					     int max_stack)
 {
-	u8 cpumode = PERF_RECORD_MISC_USER;
 	int chain_nr = min(max_stack, (int)chain->nr);
-	int i;
-	int j;
-	int err;
+	int i, j, err;
 	int skip_idx = -1;
 	int first_call = 0;
 
@@ -1542,10 +1566,10 @@ static int thread__resolve_callchain_sample(struct thread *thread,
 
 		for (i = 0; i < nr; i++) {
 			err = add_callchain_ip(thread, parent, root_al,
-					       -1, be[i].to);
+					       true, be[i].to);
 			if (!err)
 				err = add_callchain_ip(thread, parent, root_al,
-						       -1, be[i].from);
+						       true, be[i].from);
 			if (err == -EINVAL)
 				break;
 			if (err)
@@ -1574,36 +1598,10 @@ check_calls:
 #endif
 		ip = chain->ips[j];
 
-		if (ip >= PERF_CONTEXT_MAX) {
-			switch (ip) {
-			case PERF_CONTEXT_HV:
-				cpumode = PERF_RECORD_MISC_HYPERVISOR;
-				break;
-			case PERF_CONTEXT_KERNEL:
-				cpumode = PERF_RECORD_MISC_KERNEL;
-				break;
-			case PERF_CONTEXT_USER:
-				cpumode = PERF_RECORD_MISC_USER;
-				break;
-			default:
-				pr_debug("invalid callchain context: "
-					 "%"PRId64"\n", (s64) ip);
-				/*
-				 * It seems the callchain is corrupted.
-				 * Discard all.
-				 */
-				callchain_cursor_reset(&callchain_cursor);
-				return 0;
-			}
-			continue;
-		}
+		err = add_callchain_ip(thread, parent, root_al, false, ip);
 
-		err = add_callchain_ip(thread, parent, root_al,
-				       cpumode, ip);
-		if (err == -EINVAL)
-			break;
 		if (err)
-			return err;
+			return (err < 0) ? err : 0;
 	}
 
 	return 0;
Signature · 1 line
1.8.3.2
#8 lore raw

PATCH v4 3/3 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_back

If 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
index 9797909..1bbaa74 100644
@@ -368,4 +368,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