forked from mirrors/linux
		
	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
	
	 Jin Yao
						Jin Yao