aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJin Yao <yao.jin@linux.intel.com>2017-07-24 07:09:07 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2017-07-25 21:46:35 -0400
commita1a8bed32de197801bb861fbf13cd01496df3e05 (patch)
tree10ddde2045582ca31f2b6041f6cc526503c10d1d
parentb49a821ed9e05fa0ccbaec2555052b2a920be517 (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.h11
-rw-r--r--tools/perf/util/callchain.c148
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
7struct branch_type_stat { 7struct 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
15struct branch_flags; 16struct 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
1239static int count_float_printf(int idx, const char *str, float value, char *bf, int bfsize) 1264static 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
1248static int counts_str_build(char *bf, int bfsize, 1277static 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
1306static 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
1333static 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
1296static int callchain_counts_printf(FILE *fp, char *bf, int bfsize, 1358static int callchain_counts_printf(FILE *fp, char *bf, int bfsize,