mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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
					
				
					 2 changed files with 110 additions and 13 deletions
				
			
		| 
						 | 
					@ -182,6 +182,10 @@ OPTIONS
 | 
				
			||||||
--tid=::
 | 
					--tid=::
 | 
				
			||||||
	Only diff samples for given thread ID (comma separated list).
 | 
						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
 | 
					COMPARISON
 | 
				
			||||||
----------
 | 
					----------
 | 
				
			||||||
The comparison is governed by the baseline file. The baseline perf.data
 | 
					The comparison is governed by the baseline file. The baseline perf.data
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@
 | 
				
			||||||
#include "util/map.h"
 | 
					#include "util/map.h"
 | 
				
			||||||
#include "util/spark.h"
 | 
					#include "util/spark.h"
 | 
				
			||||||
#include "util/block-info.h"
 | 
					#include "util/block-info.h"
 | 
				
			||||||
 | 
					#include "util/stream.h"
 | 
				
			||||||
#include <linux/err.h>
 | 
					#include <linux/err.h>
 | 
				
			||||||
#include <linux/zalloc.h>
 | 
					#include <linux/zalloc.h>
 | 
				
			||||||
#include <subcmd/pager.h>
 | 
					#include <subcmd/pager.h>
 | 
				
			||||||
| 
						 | 
					@ -42,6 +43,7 @@ struct perf_diff {
 | 
				
			||||||
	int				 range_size;
 | 
						int				 range_size;
 | 
				
			||||||
	int				 range_num;
 | 
						int				 range_num;
 | 
				
			||||||
	bool				 has_br_stack;
 | 
						bool				 has_br_stack;
 | 
				
			||||||
 | 
						bool				 stream;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Diff command specific HPP columns. */
 | 
					/* Diff command specific HPP columns. */
 | 
				
			||||||
| 
						 | 
					@ -72,6 +74,7 @@ struct data__file {
 | 
				
			||||||
	struct perf_data	 data;
 | 
						struct perf_data	 data;
 | 
				
			||||||
	int			 idx;
 | 
						int			 idx;
 | 
				
			||||||
	struct hists		*hists;
 | 
						struct hists		*hists;
 | 
				
			||||||
 | 
						struct evlist_streams	*evlist_streams;
 | 
				
			||||||
	struct diff_hpp_fmt	 fmt[PERF_HPP_DIFF__MAX_INDEX];
 | 
						struct diff_hpp_fmt	 fmt[PERF_HPP_DIFF__MAX_INDEX];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,6 +109,7 @@ enum {
 | 
				
			||||||
	COMPUTE_DELTA_ABS,
 | 
						COMPUTE_DELTA_ABS,
 | 
				
			||||||
	COMPUTE_CYCLES,
 | 
						COMPUTE_CYCLES,
 | 
				
			||||||
	COMPUTE_MAX,
 | 
						COMPUTE_MAX,
 | 
				
			||||||
 | 
						COMPUTE_STREAM,	/* After COMPUTE_MAX to avoid use current compute arrays */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const char *compute_names[COMPUTE_MAX] = {
 | 
					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 perf_diff *pdiff = container_of(tool, struct perf_diff, tool);
 | 
				
			||||||
	struct addr_location al;
 | 
						struct addr_location al;
 | 
				
			||||||
	struct hists *hists = evsel__hists(evsel);
 | 
						struct hists *hists = evsel__hists(evsel);
 | 
				
			||||||
 | 
						struct hist_entry_iter iter = {
 | 
				
			||||||
 | 
							.evsel	= evsel,
 | 
				
			||||||
 | 
							.sample	= sample,
 | 
				
			||||||
 | 
							.ops	= &hist_iter_normal,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
	int ret = -1;
 | 
						int ret = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (perf_time__ranges_skip_sample(pdiff->ptime_range, pdiff->range_num,
 | 
						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;
 | 
							goto out_put;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (compute != COMPUTE_CYCLES) {
 | 
						switch (compute) {
 | 
				
			||||||
		if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample,
 | 
						case COMPUTE_CYCLES:
 | 
				
			||||||
				      true)) {
 | 
					 | 
				
			||||||
			pr_warning("problem incrementing symbol period, "
 | 
					 | 
				
			||||||
				   "skipping event\n");
 | 
					 | 
				
			||||||
			goto out_put;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		if (!hists__add_entry_ops(hists, &block_hist_ops, &al, NULL,
 | 
							if (!hists__add_entry_ops(hists, &block_hist_ops, &al, NULL,
 | 
				
			||||||
					  NULL, NULL, sample, true)) {
 | 
										  NULL, NULL, sample, true)) {
 | 
				
			||||||
			pr_warning("problem incrementing symbol period, "
 | 
								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,
 | 
							hist__account_cycles(sample->branch_stack, &al, sample, false,
 | 
				
			||||||
				     NULL);
 | 
									     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)
 | 
					static void data__free(struct data__file *d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int col;
 | 
						int col;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (d->evlist_streams)
 | 
				
			||||||
 | 
							evlist_streams__delete(d->evlist_streams);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
 | 
						for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
 | 
				
			||||||
		struct diff_hpp_fmt *fmt = &d->fmt[col];
 | 
							struct diff_hpp_fmt *fmt = &d->fmt[col];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1153,9 +1218,19 @@ static int __cmd_diff(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (pdiff.ptime_range)
 | 
							if (pdiff.ptime_range)
 | 
				
			||||||
			zfree(&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:
 | 
					 out_delete:
 | 
				
			||||||
	data__for_each_file(i, d) {
 | 
						data__for_each_file(i, d) {
 | 
				
			||||||
| 
						 | 
					@ -1228,6 +1303,8 @@ static const struct option options[] = {
 | 
				
			||||||
		   "only consider symbols in these pids"),
 | 
							   "only consider symbols in these pids"),
 | 
				
			||||||
	OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
 | 
						OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
 | 
				
			||||||
		   "only consider symbols in these tids"),
 | 
							   "only consider symbols in these tids"),
 | 
				
			||||||
 | 
						OPT_BOOLEAN(0, "stream", &pdiff.stream,
 | 
				
			||||||
 | 
							    "Enable hot streams comparison."),
 | 
				
			||||||
	OPT_END()
 | 
						OPT_END()
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1887,6 +1964,9 @@ int cmd_diff(int argc, const char **argv)
 | 
				
			||||||
	if (cycles_hist && (compute != COMPUTE_CYCLES))
 | 
						if (cycles_hist && (compute != COMPUTE_CYCLES))
 | 
				
			||||||
		usage_with_options(diff_usage, options);
 | 
							usage_with_options(diff_usage, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pdiff.stream)
 | 
				
			||||||
 | 
							compute = COMPUTE_STREAM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	symbol__annotation_init();
 | 
						symbol__annotation_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (symbol__init(NULL) < 0)
 | 
						if (symbol__init(NULL) < 0)
 | 
				
			||||||
| 
						 | 
					@ -1898,13 +1978,26 @@ int cmd_diff(int argc, const char **argv)
 | 
				
			||||||
	if (check_file_brstack() < 0)
 | 
						if (check_file_brstack() < 0)
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (compute == COMPUTE_CYCLES && !pdiff.has_br_stack)
 | 
						if ((compute == COMPUTE_CYCLES || compute == COMPUTE_STREAM)
 | 
				
			||||||
 | 
						    && !pdiff.has_br_stack) {
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ui_init() < 0)
 | 
						if (compute == COMPUTE_STREAM) {
 | 
				
			||||||
		return -1;
 | 
							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)
 | 
						if (setup_sorting(NULL) < 0)
 | 
				
			||||||
		usage_with_options(diff_usage, options);
 | 
							usage_with_options(diff_usage, options);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue