[PATCH v3 6/7] perf diff: Print the basic block cycles diff

From: Jin Yao
Date: Mon Jun 17 2019 - 01:06:37 EST


perf record -b ./div
perf record -b ./div

Following is the default perf diff output

# perf diff

# Event 'cycles'
#
# Baseline Delta Abs Shared Object Symbol
# ........ ......... ................ ....................................
#
49.03% +0.30% div [.] main
16.29% -0.20% libc-2.23.so [.] __random
18.82% -0.07% libc-2.23.so [.] __random_r
8.11% -0.04% div [.] compute_flag
2.25% +0.01% div [.] rand@plt
0.00% +0.01% [kernel.vmlinux] [k] task_tick_fair
5.46% +0.01% libc-2.23.so [.] rand
0.01% -0.01% [kernel.vmlinux] [k] native_irq_return_iret
0.00% -0.00% [kernel.vmlinux] [k] interrupt_entry

This patch creates a new computation selection 'cycles'.

# perf diff -c cycles

# Event 'cycles'
#
# Baseline Block cycles diff [start:end] Shared Object Symbol
# ........ .................................... ................ ....................................
#
49.03% -9 [ 4ef: 520] div [.] main
49.03% 0 [ 4e8: 4ea] div [.] main
49.03% 0 [ 4ef: 500] div [.] main
49.03% 0 [ 4ef: 51c] div [.] main
49.03% 0 [ 4ef: 535] div [.] main
18.82% 0 [ 3ac40: 3ac4d] libc-2.23.so [.] __random_r
18.82% 0 [ 3ac40: 3ac5c] libc-2.23.so [.] __random_r
18.82% 0 [ 3ac40: 3ac76] libc-2.23.so [.] __random_r
18.82% 0 [ 3ac40: 3ac88] libc-2.23.so [.] __random_r
18.82% 0 [ 3ac90: 3ac9c] libc-2.23.so [.] __random_r
16.29% -8 [ 3aac0: 3aac0] libc-2.23.so [.] __random
16.29% 0 [ 3aac0: 3aad2] libc-2.23.so [.] __random
16.29% 0 [ 3aae0: 3aae7] libc-2.23.so [.] __random
16.29% 0 [ 3ab03: 3ab0f] libc-2.23.so [.] __random
16.29% 0 [ 3ab14: 3ab1b] libc-2.23.so [.] __random
16.29% 0 [ 3ab28: 3ab2e] libc-2.23.so [.] __random
16.29% 0 [ 3ab4a: 3ab53] libc-2.23.so [.] __random
8.11% 0 [ 640: 644] div [.] compute_flag
8.11% 0 [ 649: 659] div [.] compute_flag
5.46% 0 [ 3af60: 3af60] libc-2.23.so [.] rand
5.46% 0 [ 3af60: 3af64] libc-2.23.so [.] rand
2.25% 0 [ 490: 490] div [.] rand@plt
0.01% 26 [ c00a27: c00a27] [kernel.vmlinux] [k] native_irq_return_iret
0.00% -157 [ 2bf9f2: 2bfa63] [kernel.vmlinux] [k] update_blocked_averages
0.00% -56 [ 2bf980: 2bf9d3] [kernel.vmlinux] [k] update_blocked_averages
0.00% 48 [ 2bf934: 2bf942] [kernel.vmlinux] [k] update_blocked_averages
0.00% 3 [ 2bfb38: 2bfb67] [kernel.vmlinux] [k] update_blocked_averages
0.00% 0 [ 2bf968: 2bf97b] [kernel.vmlinux] [k] update_blocked_averages

"[start:end]" indicates the basic block range. The output is sorted
by "Baseline" and the basic blocks in the same function are sorted
by cycles diff.

v3:
---
Cast 'struct hist_entry' to 'struct block_hist' in hist_entry__block_fprintf.
Use symbol_conf.report_block to check if executing hist_entry__block_fprintf.

v2:
---
Keep standard perf diff format and display the 'Baseline' and
'Shared Object'.

Signed-off-by: Jin Yao <yao.jin@xxxxxxxxxxxxxxx>
---
tools/perf/builtin-diff.c | 59 ++++++++++++++++++++++++++++++++++++++++---
tools/perf/ui/stdio/hist.c | 27 ++++++++++++++++++++
tools/perf/util/hist.c | 18 +++++++++++++
tools/perf/util/hist.h | 3 +++
tools/perf/util/symbol_conf.h | 1 +
5 files changed, 104 insertions(+), 4 deletions(-)

diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 5f5a9a9..a56d941 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -46,6 +46,7 @@ enum {
PERF_HPP_DIFF__WEIGHTED_DIFF,
PERF_HPP_DIFF__FORMULA,
PERF_HPP_DIFF__DELTA_ABS,
+ PERF_HPP_DIFF__CYCLES,

PERF_HPP_DIFF__MAX_INDEX
};
@@ -114,6 +115,7 @@ static int compute_2_hpp[COMPUTE_MAX] = {
[COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS,
[COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
[COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
+ [COMPUTE_CYCLES] = PERF_HPP_DIFF__CYCLES,
};

#define MAX_COL_WIDTH 70
@@ -152,6 +154,10 @@ static struct header_column {
[PERF_HPP_DIFF__FORMULA] = {
.name = "Formula",
.width = MAX_COL_WIDTH,
+ },
+ [PERF_HPP_DIFF__CYCLES] = {
+ .name = "Block cycles diff [start:end]",
+ .width = 36,
}
};

@@ -239,8 +245,6 @@ static int setup_compute(const struct option *opt, const char *str,
for (i = 0; i < COMPUTE_MAX; i++)
if (!strcmp(cstr, compute_names[i])) {
*cp = i;
- if (i == COMPUTE_CYCLES)
- break;
return setup_compute_opt(option);
}

@@ -1002,6 +1006,9 @@ static void hists__process(struct hists *hists)
hists__precompute(hists);
hists__output_resort(hists, NULL);

+ if (compute == COMPUTE_CYCLES)
+ symbol_conf.report_block = true;
+
hists__fprintf(hists, !quiet, 0, 0, 0, stdout,
!symbol_conf.use_callchain);
}
@@ -1257,7 +1264,7 @@ static const struct option options[] = {
OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
"Show only items with match in baseline"),
OPT_CALLBACK('c', "compute", &compute,
- "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs)",
+ "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs),cycles",
"Entries differential computation selection",
setup_compute),
OPT_BOOLEAN('p', "period", &show_period,
@@ -1335,6 +1342,29 @@ static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
return ret;
}

+static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
+ struct perf_hpp *hpp, int width)
+{
+ struct block_hist *bh = container_of(he, struct block_hist, he);
+ struct block_hist *bh_pair = container_of(pair, struct block_hist, he);
+ struct hist_entry *block_he;
+ struct block_info *bi;
+ char buf[128];
+
+ block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx);
+ if (!block_he) {
+ hpp->skip = true;
+ return 0;
+ }
+
+ bi = block_he->block_info;
+ scnprintf(buf, sizeof(buf), "%ld [%12lx:%12lx]",
+ block_he->diff.cycles, bi->sym->start + bi->start,
+ bi->sym->start + bi->end);
+
+ return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
+}
+
static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
struct perf_hpp *hpp, struct hist_entry *he,
int comparison_method)
@@ -1346,8 +1376,17 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
s64 wdiff;
char pfmt[20] = " ";

- if (!pair)
+ if (!pair) {
+ if (comparison_method == COMPUTE_CYCLES) {
+ struct block_hist *bh;
+
+ bh = container_of(he, struct block_hist, he);
+ if (bh->block_idx)
+ hpp->skip = true;
+ }
+
goto no_print;
+ }

switch (comparison_method) {
case COMPUTE_DELTA:
@@ -1382,6 +1421,8 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
return color_snprintf(hpp->buf, hpp->size,
get_percent_color(wdiff),
pfmt, wdiff);
+ case COMPUTE_CYCLES:
+ return cycles_printf(he, pair, hpp, dfmt->header_width);
default:
BUG_ON(1);
}
@@ -1411,6 +1452,12 @@ static int hpp__color_wdiff(struct perf_hpp_fmt *fmt,
return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
}

+static int hpp__color_cycles(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp, struct hist_entry *he)
+{
+ return __hpp__color_compare(fmt, hpp, he, COMPUTE_CYCLES);
+}
+
static void
hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
{
@@ -1612,6 +1659,10 @@ static void data__hpp_register(struct data__file *d, int idx)
fmt->color = hpp__color_delta;
fmt->sort = hist_entry__cmp_delta_abs;
break;
+ case PERF_HPP_DIFF__CYCLES:
+ fmt->color = hpp__color_cycles;
+ fmt->sort = hist_entry__cmp_nop;
+ break;
default:
fmt->sort = hist_entry__cmp_nop;
break;
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index a60f299..4e74bb7 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -531,6 +531,30 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
return printed;
}

+static int hist_entry__block_fprintf(struct hist_entry *he,
+ char *bf, size_t size,
+ FILE *fp)
+{
+ struct block_hist *bh = container_of(he, struct block_hist, he);
+ int ret = 0;
+
+ for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) {
+ struct perf_hpp hpp = {
+ .buf = bf,
+ .size = size,
+ .skip = false,
+ };
+
+ bh->block_idx = i;
+ hist_entry__snprintf(he, &hpp);
+
+ if (!hpp.skip)
+ ret += fprintf(fp, "%s\n", bf);
+ }
+
+ return ret;
+}
+
static int hist_entry__fprintf(struct hist_entry *he, size_t size,
char *bf, size_t bfsz, FILE *fp,
bool ignore_callchains)
@@ -550,6 +574,9 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
if (symbol_conf.report_hierarchy)
return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);

+ if (symbol_conf.report_block)
+ return hist_entry__block_fprintf(he, bf, size, fp);
+
hist_entry__snprintf(he, &hpp);

ret = fprintf(fp, "%s\n", bf);
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 680ad93..2fb9cb2 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -376,6 +376,24 @@ void hists__delete_entries(struct hists *hists)
}
}

+struct hist_entry *hists__get_entry(struct hists *hists, int idx)
+{
+ struct rb_node *next = rb_first_cached(&hists->entries);
+ struct hist_entry *n;
+ int i = 0;
+
+ while (next) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ if (i == idx)
+ return n;
+
+ next = rb_next(&n->rb_node);
+ i++;
+ }
+
+ return NULL;
+}
+
/*
* histogram, sorted on item, collects periods
*/
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index c8f7d66..9124b54 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -184,6 +184,8 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
void hists__delete_entries(struct hists *hists);
void hists__output_recalc_col_len(struct hists *hists, int max_rows);

+struct hist_entry *hists__get_entry(struct hists *hists, int idx);
+
u64 hists__total_period(struct hists *hists);
void hists__reset_stats(struct hists *hists);
void hists__inc_stats(struct hists *hists, struct hist_entry *h);
@@ -249,6 +251,7 @@ struct perf_hpp {
size_t size;
const char *sep;
void *ptr;
+ bool skip;
};

struct perf_hpp_fmt {
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index 382ba63..be4b244 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -39,6 +39,7 @@ struct symbol_conf {
hide_unresolved,
raw_trace,
report_hierarchy,
+ report_block,
inline_name;
const char *vmlinux_name,
*kallsyms_name,
--
2.7.4