perf diff: Support hot streams comparison
This patch enables perf-diff with "--stream" option. "--stream": Enable hot streams comparison Now let's see example. perf record -b ... Generate perf.data.old with branch data perf record -b ... Generate perf.data with branch data perf diff --stream [ Matched hot streams ] hot chain pair 1: cycles: 1, hits: 27.77% cycles: 1, hits: 9.24% --------------------------- -------------------------- main div.c:39 main div.c:39 main div.c:44 main div.c:44 hot chain pair 2: cycles: 34, hits: 20.06% cycles: 27, hits: 16.98% --------------------------- -------------------------- __random_r random_r.c:360 __random_r random_r.c:360 __random_r random_r.c:388 __random_r random_r.c:388 __random_r random_r.c:388 __random_r random_r.c:388 __random_r random_r.c:380 __random_r random_r.c:380 __random_r random_r.c:357 __random_r random_r.c:357 __random random.c:293 __random random.c:293 __random random.c:293 __random random.c:293 __random random.c:291 __random random.c:291 __random random.c:291 __random random.c:291 __random random.c:291 __random random.c:291 __random random.c:288 __random random.c:288 rand rand.c:27 rand rand.c:27 rand rand.c:26 rand rand.c:26 rand@plt rand@plt rand@plt rand@plt compute_flag div.c:25 compute_flag div.c:25 compute_flag div.c:22 compute_flag div.c:22 main div.c:40 main div.c:40 main div.c:40 main div.c:40 main div.c:39 main div.c:39 hot chain pair 3: cycles: 9, hits: 4.48% cycles: 6, hits: 4.51% --------------------------- -------------------------- __random_r random_r.c:360 __random_r random_r.c:360 __random_r random_r.c:388 __random_r random_r.c:388 __random_r random_r.c:388 __random_r random_r.c:388 __random_r random_r.c:380 __random_r random_r.c:380 [ Hot streams in old perf data only ] hot chain 1: cycles: 18, hits: 6.75% -------------------------- __random_r random_r.c:360 __random_r random_r.c:388 __random_r random_r.c:388 __random_r random_r.c:380 __random_r random_r.c:357 __random random.c:293 __random random.c:293 __random random.c:291 __random random.c:291 __random random.c:291 __random random.c:288 rand rand.c:27 rand rand.c:26 rand@plt rand@plt compute_flag div.c:25 compute_flag div.c:22 main div.c:40 hot chain 2: cycles: 29, hits: 2.78% -------------------------- compute_flag div.c:22 main div.c:40 main div.c:40 main div.c:39 [ Hot streams in new perf data only ] hot chain 1: cycles: 4, hits: 4.54% -------------------------- main div.c:42 compute_flag div.c:28 hot chain 2: cycles: 5, hits: 3.51% -------------------------- main div.c:39 main div.c:44 main div.c:42 compute_flag div.c:28 Signed-off-by: Jin Yao <yao.jin@linux.intel.com> Acked-by: Jiri Olsa <jolsa@kernel.org> Link: https://lore.kernel.org/r/20201009022845.13141-8-yao.jin@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
5bbd6bad3b
commit
2a09a84c72
|
@ -182,6 +182,10 @@ OPTIONS
|
|||
--tid=::
|
||||
Only diff samples for given thread ID (comma separated list).
|
||||
|
||||
--stream::
|
||||
Enable hot streams comparison. Stream can be a callchain which is
|
||||
aggregated by the branch records from samples.
|
||||
|
||||
COMPARISON
|
||||
----------
|
||||
The comparison is governed by the baseline file. The baseline perf.data
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "util/map.h"
|
||||
#include "util/spark.h"
|
||||
#include "util/block-info.h"
|
||||
#include "util/stream.h"
|
||||
#include <linux/err.h>
|
||||
#include <linux/zalloc.h>
|
||||
#include <subcmd/pager.h>
|
||||
|
@ -42,6 +43,7 @@ struct perf_diff {
|
|||
int range_size;
|
||||
int range_num;
|
||||
bool has_br_stack;
|
||||
bool stream;
|
||||
};
|
||||
|
||||
/* Diff command specific HPP columns. */
|
||||
|
@ -72,6 +74,7 @@ struct data__file {
|
|||
struct perf_data data;
|
||||
int idx;
|
||||
struct hists *hists;
|
||||
struct evlist_streams *evlist_streams;
|
||||
struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX];
|
||||
};
|
||||
|
||||
|
@ -106,6 +109,7 @@ enum {
|
|||
COMPUTE_DELTA_ABS,
|
||||
COMPUTE_CYCLES,
|
||||
COMPUTE_MAX,
|
||||
COMPUTE_STREAM, /* After COMPUTE_MAX to avoid use current compute arrays */
|
||||
};
|
||||
|
||||
const char *compute_names[COMPUTE_MAX] = {
|
||||
|
@ -393,6 +397,11 @@ static int diff__process_sample_event(struct perf_tool *tool,
|
|||
struct perf_diff *pdiff = container_of(tool, struct perf_diff, tool);
|
||||
struct addr_location al;
|
||||
struct hists *hists = evsel__hists(evsel);
|
||||
struct hist_entry_iter iter = {
|
||||
.evsel = evsel,
|
||||
.sample = sample,
|
||||
.ops = &hist_iter_normal,
|
||||
};
|
||||
int ret = -1;
|
||||
|
||||
if (perf_time__ranges_skip_sample(pdiff->ptime_range, pdiff->range_num,
|
||||
|
@ -411,14 +420,8 @@ static int diff__process_sample_event(struct perf_tool *tool,
|
|||
goto out_put;
|
||||
}
|
||||
|
||||
if (compute != COMPUTE_CYCLES) {
|
||||
if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample,
|
||||
true)) {
|
||||
pr_warning("problem incrementing symbol period, "
|
||||
"skipping event\n");
|
||||
goto out_put;
|
||||
}
|
||||
} else {
|
||||
switch (compute) {
|
||||
case COMPUTE_CYCLES:
|
||||
if (!hists__add_entry_ops(hists, &block_hist_ops, &al, NULL,
|
||||
NULL, NULL, sample, true)) {
|
||||
pr_warning("problem incrementing symbol period, "
|
||||
|
@ -428,6 +431,23 @@ static int diff__process_sample_event(struct perf_tool *tool,
|
|||
|
||||
hist__account_cycles(sample->branch_stack, &al, sample, false,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case COMPUTE_STREAM:
|
||||
if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
|
||||
NULL)) {
|
||||
pr_debug("problem adding hist entry, skipping event\n");
|
||||
goto out_put;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample,
|
||||
true)) {
|
||||
pr_warning("problem incrementing symbol period, "
|
||||
"skipping event\n");
|
||||
goto out_put;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -996,10 +1016,55 @@ static void data_process(void)
|
|||
}
|
||||
}
|
||||
|
||||
static int process_base_stream(struct data__file *data_base,
|
||||
struct data__file *data_pair,
|
||||
const char *title __maybe_unused)
|
||||
{
|
||||
struct evlist *evlist_base = data_base->session->evlist;
|
||||
struct evlist *evlist_pair = data_pair->session->evlist;
|
||||
struct evsel *evsel_base, *evsel_pair;
|
||||
struct evsel_streams *es_base, *es_pair;
|
||||
|
||||
evlist__for_each_entry(evlist_base, evsel_base) {
|
||||
evsel_pair = evsel_match(evsel_base, evlist_pair);
|
||||
if (!evsel_pair)
|
||||
continue;
|
||||
|
||||
es_base = evsel_streams__entry(data_base->evlist_streams,
|
||||
evsel_base->idx);
|
||||
if (!es_base)
|
||||
return -1;
|
||||
|
||||
es_pair = evsel_streams__entry(data_pair->evlist_streams,
|
||||
evsel_pair->idx);
|
||||
if (!es_pair)
|
||||
return -1;
|
||||
|
||||
evsel_streams__match(es_base, es_pair);
|
||||
evsel_streams__report(es_base, es_pair);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stream_process(void)
|
||||
{
|
||||
/*
|
||||
* Stream comparison only supports two data files.
|
||||
* perf.data.old and perf.data. data__files[0] is perf.data.old,
|
||||
* data__files[1] is perf.data.
|
||||
*/
|
||||
process_base_stream(&data__files[0], &data__files[1],
|
||||
"# Output based on old perf data:\n#\n");
|
||||
}
|
||||
|
||||
static void data__free(struct data__file *d)
|
||||
{
|
||||
int col;
|
||||
|
||||
if (d->evlist_streams)
|
||||
evlist_streams__delete(d->evlist_streams);
|
||||
|
||||
for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
|
||||
struct diff_hpp_fmt *fmt = &d->fmt[col];
|
||||
|
||||
|
@ -1153,9 +1218,19 @@ static int __cmd_diff(void)
|
|||
|
||||
if (pdiff.ptime_range)
|
||||
zfree(&pdiff.ptime_range);
|
||||
|
||||
if (compute == COMPUTE_STREAM) {
|
||||
d->evlist_streams = evlist__create_streams(
|
||||
d->session->evlist, 5);
|
||||
if (!d->evlist_streams)
|
||||
goto out_delete;
|
||||
}
|
||||
}
|
||||
|
||||
data_process();
|
||||
if (compute == COMPUTE_STREAM)
|
||||
stream_process();
|
||||
else
|
||||
data_process();
|
||||
|
||||
out_delete:
|
||||
data__for_each_file(i, d) {
|
||||
|
@ -1228,6 +1303,8 @@ static const struct option options[] = {
|
|||
"only consider symbols in these pids"),
|
||||
OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
|
||||
"only consider symbols in these tids"),
|
||||
OPT_BOOLEAN(0, "stream", &pdiff.stream,
|
||||
"Enable hot streams comparison."),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -1887,6 +1964,9 @@ int cmd_diff(int argc, const char **argv)
|
|||
if (cycles_hist && (compute != COMPUTE_CYCLES))
|
||||
usage_with_options(diff_usage, options);
|
||||
|
||||
if (pdiff.stream)
|
||||
compute = COMPUTE_STREAM;
|
||||
|
||||
symbol__annotation_init();
|
||||
|
||||
if (symbol__init(NULL) < 0)
|
||||
|
@ -1898,13 +1978,26 @@ int cmd_diff(int argc, const char **argv)
|
|||
if (check_file_brstack() < 0)
|
||||
return -1;
|
||||
|
||||
if (compute == COMPUTE_CYCLES && !pdiff.has_br_stack)
|
||||
if ((compute == COMPUTE_CYCLES || compute == COMPUTE_STREAM)
|
||||
&& !pdiff.has_br_stack) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ui_init() < 0)
|
||||
return -1;
|
||||
if (compute == COMPUTE_STREAM) {
|
||||
symbol_conf.show_branchflag_count = true;
|
||||
symbol_conf.disable_add2line_warn = true;
|
||||
callchain_param.mode = CHAIN_FLAT;
|
||||
callchain_param.key = CCKEY_SRCLINE;
|
||||
callchain_param.branch_callstack = 1;
|
||||
symbol_conf.use_callchain = true;
|
||||
callchain_register_param(&callchain_param);
|
||||
sort_order = "srcline,symbol,dso";
|
||||
} else {
|
||||
if (ui_init() < 0)
|
||||
return -1;
|
||||
|
||||
sort__mode = SORT_MODE__DIFF;
|
||||
sort__mode = SORT_MODE__DIFF;
|
||||
}
|
||||
|
||||
if (setup_sorting(NULL) < 0)
|
||||
usage_with_options(diff_usage, options);
|
||||
|
|
Loading…
Reference in New Issue