diff options
| author | Jin Yao <yao.jin@linux.intel.com> | 2017-07-24 07:09:07 -0400 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2017-07-25 21:46:35 -0400 |
| commit | a1a8bed32de197801bb861fbf13cd01496df3e05 (patch) | |
| tree | 10ddde2045582ca31f2b6041f6cc526503c10d1d | |
| parent | b49a821ed9e05fa0ccbaec2555052b2a920be517 (diff) | |
perf report: Tag branch type/flag on "to" and tag cycles on "from"
Current --branch-history LBR annotation displays confused data. For
example, each cycles report is duplicated on both "from" and "to"
entries.
For example:
perf report --branch-history --no-children --stdio
--2.32%--main div.c:39 (COND_BWD CROSS_2M predicted:49.7% cycles:1)
main div.c:44 (predicted:49.7% cycles:1)
main div.c:42 (RET CROSS_2M cycles:2)
compute_flag div.c:28 (cycles:2)
compute_flag div.c:27 (RET CROSS_2M cycles:1)
rand rand.c:28 (cycles:1)
rand rand.c:28 (RET CROSS_2M cycles:1)
__random random.c:298 (cycles:1)
__random random.c:297 (COND_BWD CROSS_2M cycles:1)
__random random.c:295 (cycles:1)
__random random.c:295 (COND_BWD CROSS_2M cycles:1)
__random random.c:295 (cycles:1)
__random random.c:295 (RET CROSS_2M cycles:9)
The cycles should be tagged only on the "from". It's for the code block
that ends with "from", not for "to".
Another issue is the "predicted:49.7%" is duplicated too (tag on both
"from" and "to").
This patch tags the branch type/flag on "to" and tag the cycles on
"from".
For example:
--2.32%--main div.c:39 (COND_BWD CROSS_2M predicted:49.7%)
main div.c:44 (cycles:1)
main div.c:42 (RET CROSS_2M)
compute_flag div.c:28 (cycles:2)
compute_flag div.c:27 (RET CROSS_2M)
rand rand.c:28 (cycles:1)
rand rand.c:28 (RET CROSS_2M)
__random random.c:298 (cycles:1)
__random random.c:297 (COND_BWD CROSS_2M)
__random random.c:295 (cycles:1)
__random random.c:295 (COND_BWD CROSS_2M)
__random random.c:295 (cycles:1)
__random random.c:295 (RET CROSS_2M)
|
--2.23%--__random_r random_r.c:392 (cycles:9)
In this example, The "main div.c:39 (COND_BWD CROSS_2M predicted:49.7%)"
is "to" of branch and "main div.c:44 (cycles:1)" is "from" of branch.
It should be easier for understanding than before.
Signed-off-by: Yao Jin <yao.jin@linux.intel.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/1500894547-18411-1-git-send-email-yao.jin@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
| -rw-r--r-- | tools/perf/util/branch.h | 11 | ||||
| -rw-r--r-- | tools/perf/util/callchain.c | 148 |
2 files changed, 111 insertions, 48 deletions
diff --git a/tools/perf/util/branch.h b/tools/perf/util/branch.h index 686f2b65ba84..1e3c7c5cdc63 100644 --- a/tools/perf/util/branch.h +++ b/tools/perf/util/branch.h | |||
| @@ -5,11 +5,12 @@ | |||
| 5 | #include "../perf.h" | 5 | #include "../perf.h" |
| 6 | 6 | ||
| 7 | struct branch_type_stat { | 7 | struct branch_type_stat { |
| 8 | u64 counts[PERF_BR_MAX]; | 8 | bool branch_to; |
| 9 | u64 cond_fwd; | 9 | u64 counts[PERF_BR_MAX]; |
| 10 | u64 cond_bwd; | 10 | u64 cond_fwd; |
| 11 | u64 cross_4k; | 11 | u64 cond_bwd; |
| 12 | u64 cross_2m; | 12 | u64 cross_4k; |
| 13 | u64 cross_2m; | ||
| 13 | }; | 14 | }; |
| 14 | 15 | ||
| 15 | struct branch_flags; | 16 | struct branch_flags; |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 1f536418dfb5..f320b0777e0d 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
| @@ -563,20 +563,33 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) | |||
| 563 | if (cursor_node->branch) { | 563 | if (cursor_node->branch) { |
| 564 | call->branch_count = 1; | 564 | call->branch_count = 1; |
| 565 | 565 | ||
| 566 | if (cursor_node->branch_flags.predicted) | 566 | if (cursor_node->branch_from) { |
| 567 | call->predicted_count = 1; | 567 | /* |
| 568 | 568 | * branch_from is set with value somewhere else | |
| 569 | if (cursor_node->branch_flags.abort) | 569 | * to imply it's "to" of a branch. |
| 570 | call->abort_count = 1; | 570 | */ |
| 571 | 571 | call->brtype_stat.branch_to = true; | |
| 572 | call->cycles_count = cursor_node->branch_flags.cycles; | 572 | |
| 573 | call->iter_count = cursor_node->nr_loop_iter; | 573 | if (cursor_node->branch_flags.predicted) |
| 574 | call->samples_count = cursor_node->samples; | 574 | call->predicted_count = 1; |
| 575 | 575 | ||
| 576 | branch_type_count(&call->brtype_stat, | 576 | if (cursor_node->branch_flags.abort) |
| 577 | &cursor_node->branch_flags, | 577 | call->abort_count = 1; |
| 578 | cursor_node->branch_from, | 578 | |
| 579 | cursor_node->ip); | 579 | branch_type_count(&call->brtype_stat, |
| 580 | &cursor_node->branch_flags, | ||
| 581 | cursor_node->branch_from, | ||
| 582 | cursor_node->ip); | ||
| 583 | } else { | ||
| 584 | /* | ||
| 585 | * It's "from" of a branch | ||
| 586 | */ | ||
| 587 | call->brtype_stat.branch_to = false; | ||
| 588 | call->cycles_count = | ||
| 589 | cursor_node->branch_flags.cycles; | ||
| 590 | call->iter_count = cursor_node->nr_loop_iter; | ||
| 591 | call->samples_count = cursor_node->samples; | ||
| 592 | } | ||
| 580 | } | 593 | } |
| 581 | 594 | ||
| 582 | list_add_tail(&call->list, &node->val); | 595 | list_add_tail(&call->list, &node->val); |
| @@ -685,20 +698,32 @@ static enum match_result match_chain(struct callchain_cursor_node *node, | |||
| 685 | if (node->branch) { | 698 | if (node->branch) { |
| 686 | cnode->branch_count++; | 699 | cnode->branch_count++; |
| 687 | 700 | ||
| 688 | if (node->branch_flags.predicted) | 701 | if (node->branch_from) { |
| 689 | cnode->predicted_count++; | 702 | /* |
| 690 | 703 | * It's "to" of a branch | |
| 691 | if (node->branch_flags.abort) | 704 | */ |
| 692 | cnode->abort_count++; | 705 | cnode->brtype_stat.branch_to = true; |
| 693 | 706 | ||
| 694 | cnode->cycles_count += node->branch_flags.cycles; | 707 | if (node->branch_flags.predicted) |
| 695 | cnode->iter_count += node->nr_loop_iter; | 708 | cnode->predicted_count++; |
| 696 | cnode->samples_count += node->samples; | 709 | |
| 697 | 710 | if (node->branch_flags.abort) | |
| 698 | branch_type_count(&cnode->brtype_stat, | 711 | cnode->abort_count++; |
| 699 | &node->branch_flags, | 712 | |
| 700 | node->branch_from, | 713 | branch_type_count(&cnode->brtype_stat, |
| 701 | node->ip); | 714 | &node->branch_flags, |
| 715 | node->branch_from, | ||
| 716 | node->ip); | ||
| 717 | } else { | ||
| 718 | /* | ||
| 719 | * It's "from" of a branch | ||
| 720 | */ | ||
| 721 | cnode->brtype_stat.branch_to = false; | ||
| 722 | cnode->cycles_count += | ||
| 723 | node->branch_flags.cycles; | ||
| 724 | cnode->iter_count += node->nr_loop_iter; | ||
| 725 | cnode->samples_count += node->samples; | ||
| 726 | } | ||
| 702 | } | 727 | } |
| 703 | 728 | ||
| 704 | return MATCH_EQ; | 729 | return MATCH_EQ; |
| @@ -1236,27 +1261,26 @@ static int count_pri64_printf(int idx, const char *str, u64 value, char *bf, int | |||
| 1236 | return printed; | 1261 | return printed; |
| 1237 | } | 1262 | } |
| 1238 | 1263 | ||
| 1239 | static int count_float_printf(int idx, const char *str, float value, char *bf, int bfsize) | 1264 | static int count_float_printf(int idx, const char *str, float value, |
| 1265 | char *bf, int bfsize, float threshold) | ||
| 1240 | { | 1266 | { |
| 1241 | int printed; | 1267 | int printed; |
| 1242 | 1268 | ||
| 1269 | if (threshold != 0.0 && value < threshold) | ||
| 1270 | return 0; | ||
| 1271 | |||
| 1243 | printed = scnprintf(bf, bfsize, "%s%s:%.1f%%", (idx) ? " " : " (", str, value); | 1272 | printed = scnprintf(bf, bfsize, "%s%s:%.1f%%", (idx) ? " " : " (", str, value); |
| 1244 | 1273 | ||
| 1245 | return printed; | 1274 | return printed; |
| 1246 | } | 1275 | } |
| 1247 | 1276 | ||
| 1248 | static int counts_str_build(char *bf, int bfsize, | 1277 | static int branch_to_str(char *bf, int bfsize, |
| 1249 | u64 branch_count, u64 predicted_count, | 1278 | u64 branch_count, u64 predicted_count, |
| 1250 | u64 abort_count, u64 cycles_count, | 1279 | u64 abort_count, |
| 1251 | u64 iter_count, u64 samples_count, | 1280 | struct branch_type_stat *brtype_stat) |
| 1252 | struct branch_type_stat *brtype_stat) | ||
| 1253 | { | 1281 | { |
| 1254 | u64 cycles; | ||
| 1255 | int printed, i = 0; | 1282 | int printed, i = 0; |
| 1256 | 1283 | ||
| 1257 | if (branch_count == 0) | ||
| 1258 | return scnprintf(bf, bfsize, " (calltrace)"); | ||
| 1259 | |||
| 1260 | printed = branch_type_str(brtype_stat, bf, bfsize); | 1284 | printed = branch_type_str(brtype_stat, bf, bfsize); |
| 1261 | if (printed) | 1285 | if (printed) |
| 1262 | i++; | 1286 | i++; |
| @@ -1264,15 +1288,29 @@ static int counts_str_build(char *bf, int bfsize, | |||
| 1264 | if (predicted_count < branch_count) { | 1288 | if (predicted_count < branch_count) { |
| 1265 | printed += count_float_printf(i++, "predicted", | 1289 | printed += count_float_printf(i++, "predicted", |
| 1266 | predicted_count * 100.0 / branch_count, | 1290 | predicted_count * 100.0 / branch_count, |
| 1267 | bf + printed, bfsize - printed); | 1291 | bf + printed, bfsize - printed, 0.0); |
| 1268 | } | 1292 | } |
| 1269 | 1293 | ||
| 1270 | if (abort_count) { | 1294 | if (abort_count) { |
| 1271 | printed += count_float_printf(i++, "abort", | 1295 | printed += count_float_printf(i++, "abort", |
| 1272 | abort_count * 100.0 / branch_count, | 1296 | abort_count * 100.0 / branch_count, |
| 1273 | bf + printed, bfsize - printed); | 1297 | bf + printed, bfsize - printed, 0.1); |
| 1274 | } | 1298 | } |
| 1275 | 1299 | ||
| 1300 | if (i) | ||
| 1301 | printed += scnprintf(bf + printed, bfsize - printed, ")"); | ||
| 1302 | |||
| 1303 | return printed; | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | static int branch_from_str(char *bf, int bfsize, | ||
| 1307 | u64 branch_count, | ||
| 1308 | u64 cycles_count, u64 iter_count, | ||
| 1309 | u64 samples_count) | ||
| 1310 | { | ||
| 1311 | int printed = 0, i = 0; | ||
| 1312 | u64 cycles; | ||
| 1313 | |||
| 1276 | cycles = cycles_count / branch_count; | 1314 | cycles = cycles_count / branch_count; |
| 1277 | if (cycles) { | 1315 | if (cycles) { |
| 1278 | printed += count_pri64_printf(i++, "cycles", | 1316 | printed += count_pri64_printf(i++, "cycles", |
| @@ -1287,10 +1325,34 @@ static int counts_str_build(char *bf, int bfsize, | |||
| 1287 | } | 1325 | } |
| 1288 | 1326 | ||
| 1289 | if (i) | 1327 | if (i) |
| 1290 | return scnprintf(bf + printed, bfsize - printed, ")"); | 1328 | printed += scnprintf(bf + printed, bfsize - printed, ")"); |
| 1291 | 1329 | ||
| 1292 | bf[0] = 0; | 1330 | return printed; |
| 1293 | return 0; | 1331 | } |
| 1332 | |||
| 1333 | static int counts_str_build(char *bf, int bfsize, | ||
| 1334 | u64 branch_count, u64 predicted_count, | ||
| 1335 | u64 abort_count, u64 cycles_count, | ||
| 1336 | u64 iter_count, u64 samples_count, | ||
| 1337 | struct branch_type_stat *brtype_stat) | ||
| 1338 | { | ||
| 1339 | int printed; | ||
| 1340 | |||
| 1341 | if (branch_count == 0) | ||
| 1342 | return scnprintf(bf, bfsize, " (calltrace)"); | ||
| 1343 | |||
| 1344 | if (brtype_stat->branch_to) { | ||
| 1345 | printed = branch_to_str(bf, bfsize, branch_count, | ||
| 1346 | predicted_count, abort_count, brtype_stat); | ||
| 1347 | } else { | ||
| 1348 | printed = branch_from_str(bf, bfsize, branch_count, | ||
| 1349 | cycles_count, iter_count, samples_count); | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | if (!printed) | ||
| 1353 | bf[0] = 0; | ||
| 1354 | |||
| 1355 | return printed; | ||
| 1294 | } | 1356 | } |
| 1295 | 1357 | ||
| 1296 | static int callchain_counts_printf(FILE *fp, char *bf, int bfsize, | 1358 | static int callchain_counts_printf(FILE *fp, char *bf, int bfsize, |
