xref: /openbmc/linux/tools/perf/builtin-diff.c (revision 151f4e2b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * builtin-diff.c
4  *
5  * Builtin diff command: Analyze two perf.data input files, look up and read
6  * DSOs and symbol information, sort them and produce a diff.
7  */
8 #include "builtin.h"
9 
10 #include "util/debug.h"
11 #include "util/event.h"
12 #include "util/hist.h"
13 #include "util/evsel.h"
14 #include "util/evlist.h"
15 #include "util/session.h"
16 #include "util/tool.h"
17 #include "util/sort.h"
18 #include "util/symbol.h"
19 #include "util/util.h"
20 #include "util/data.h"
21 #include "util/config.h"
22 #include "util/time-utils.h"
23 
24 #include <errno.h>
25 #include <inttypes.h>
26 #include <stdlib.h>
27 #include <math.h>
28 
29 struct perf_diff {
30 	struct perf_tool		 tool;
31 	const char			*time_str;
32 	struct perf_time_interval	*ptime_range;
33 	int				 range_size;
34 	int				 range_num;
35 };
36 
37 /* Diff command specific HPP columns. */
38 enum {
39 	PERF_HPP_DIFF__BASELINE,
40 	PERF_HPP_DIFF__PERIOD,
41 	PERF_HPP_DIFF__PERIOD_BASELINE,
42 	PERF_HPP_DIFF__DELTA,
43 	PERF_HPP_DIFF__RATIO,
44 	PERF_HPP_DIFF__WEIGHTED_DIFF,
45 	PERF_HPP_DIFF__FORMULA,
46 	PERF_HPP_DIFF__DELTA_ABS,
47 
48 	PERF_HPP_DIFF__MAX_INDEX
49 };
50 
51 struct diff_hpp_fmt {
52 	struct perf_hpp_fmt	 fmt;
53 	int			 idx;
54 	char			*header;
55 	int			 header_width;
56 };
57 
58 struct data__file {
59 	struct perf_session	*session;
60 	struct perf_data	 data;
61 	int			 idx;
62 	struct hists		*hists;
63 	struct diff_hpp_fmt	 fmt[PERF_HPP_DIFF__MAX_INDEX];
64 };
65 
66 static struct data__file *data__files;
67 static int data__files_cnt;
68 
69 #define data__for_each_file_start(i, d, s)	\
70 	for (i = s, d = &data__files[s];	\
71 	     i < data__files_cnt;		\
72 	     i++, d = &data__files[i])
73 
74 #define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
75 #define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
76 
77 static bool force;
78 static bool show_period;
79 static bool show_formula;
80 static bool show_baseline_only;
81 static unsigned int sort_compute = 1;
82 
83 static s64 compute_wdiff_w1;
84 static s64 compute_wdiff_w2;
85 
86 static const char		*cpu_list;
87 static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
88 
89 enum {
90 	COMPUTE_DELTA,
91 	COMPUTE_RATIO,
92 	COMPUTE_WEIGHTED_DIFF,
93 	COMPUTE_DELTA_ABS,
94 	COMPUTE_MAX,
95 };
96 
97 const char *compute_names[COMPUTE_MAX] = {
98 	[COMPUTE_DELTA] = "delta",
99 	[COMPUTE_DELTA_ABS] = "delta-abs",
100 	[COMPUTE_RATIO] = "ratio",
101 	[COMPUTE_WEIGHTED_DIFF] = "wdiff",
102 };
103 
104 static int compute = COMPUTE_DELTA_ABS;
105 
106 static int compute_2_hpp[COMPUTE_MAX] = {
107 	[COMPUTE_DELTA]		= PERF_HPP_DIFF__DELTA,
108 	[COMPUTE_DELTA_ABS]	= PERF_HPP_DIFF__DELTA_ABS,
109 	[COMPUTE_RATIO]		= PERF_HPP_DIFF__RATIO,
110 	[COMPUTE_WEIGHTED_DIFF]	= PERF_HPP_DIFF__WEIGHTED_DIFF,
111 };
112 
113 #define MAX_COL_WIDTH 70
114 
115 static struct header_column {
116 	const char *name;
117 	int width;
118 } columns[PERF_HPP_DIFF__MAX_INDEX] = {
119 	[PERF_HPP_DIFF__BASELINE] = {
120 		.name  = "Baseline",
121 	},
122 	[PERF_HPP_DIFF__PERIOD] = {
123 		.name  = "Period",
124 		.width = 14,
125 	},
126 	[PERF_HPP_DIFF__PERIOD_BASELINE] = {
127 		.name  = "Base period",
128 		.width = 14,
129 	},
130 	[PERF_HPP_DIFF__DELTA] = {
131 		.name  = "Delta",
132 		.width = 7,
133 	},
134 	[PERF_HPP_DIFF__DELTA_ABS] = {
135 		.name  = "Delta Abs",
136 		.width = 7,
137 	},
138 	[PERF_HPP_DIFF__RATIO] = {
139 		.name  = "Ratio",
140 		.width = 14,
141 	},
142 	[PERF_HPP_DIFF__WEIGHTED_DIFF] = {
143 		.name  = "Weighted diff",
144 		.width = 14,
145 	},
146 	[PERF_HPP_DIFF__FORMULA] = {
147 		.name  = "Formula",
148 		.width = MAX_COL_WIDTH,
149 	}
150 };
151 
152 static int setup_compute_opt_wdiff(char *opt)
153 {
154 	char *w1_str = opt;
155 	char *w2_str;
156 
157 	int ret = -EINVAL;
158 
159 	if (!opt)
160 		goto out;
161 
162 	w2_str = strchr(opt, ',');
163 	if (!w2_str)
164 		goto out;
165 
166 	*w2_str++ = 0x0;
167 	if (!*w2_str)
168 		goto out;
169 
170 	compute_wdiff_w1 = strtol(w1_str, NULL, 10);
171 	compute_wdiff_w2 = strtol(w2_str, NULL, 10);
172 
173 	if (!compute_wdiff_w1 || !compute_wdiff_w2)
174 		goto out;
175 
176 	pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
177 		  compute_wdiff_w1, compute_wdiff_w2);
178 
179 	ret = 0;
180 
181  out:
182 	if (ret)
183 		pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
184 
185 	return ret;
186 }
187 
188 static int setup_compute_opt(char *opt)
189 {
190 	if (compute == COMPUTE_WEIGHTED_DIFF)
191 		return setup_compute_opt_wdiff(opt);
192 
193 	if (opt) {
194 		pr_err("Failed: extra option specified '%s'", opt);
195 		return -EINVAL;
196 	}
197 
198 	return 0;
199 }
200 
201 static int setup_compute(const struct option *opt, const char *str,
202 			 int unset __maybe_unused)
203 {
204 	int *cp = (int *) opt->value;
205 	char *cstr = (char *) str;
206 	char buf[50];
207 	unsigned i;
208 	char *option;
209 
210 	if (!str) {
211 		*cp = COMPUTE_DELTA;
212 		return 0;
213 	}
214 
215 	option = strchr(str, ':');
216 	if (option) {
217 		unsigned len = option++ - str;
218 
219 		/*
220 		 * The str data are not writeable, so we need
221 		 * to use another buffer.
222 		 */
223 
224 		/* No option value is longer. */
225 		if (len >= sizeof(buf))
226 			return -EINVAL;
227 
228 		strncpy(buf, str, len);
229 		buf[len] = 0x0;
230 		cstr = buf;
231 	}
232 
233 	for (i = 0; i < COMPUTE_MAX; i++)
234 		if (!strcmp(cstr, compute_names[i])) {
235 			*cp = i;
236 			return setup_compute_opt(option);
237 		}
238 
239 	pr_err("Failed: '%s' is not computation method "
240 	       "(use 'delta','ratio' or 'wdiff')\n", str);
241 	return -EINVAL;
242 }
243 
244 static double period_percent(struct hist_entry *he, u64 period)
245 {
246 	u64 total = hists__total_period(he->hists);
247 
248 	return (period * 100.0) / total;
249 }
250 
251 static double compute_delta(struct hist_entry *he, struct hist_entry *pair)
252 {
253 	double old_percent = period_percent(he, he->stat.period);
254 	double new_percent = period_percent(pair, pair->stat.period);
255 
256 	pair->diff.period_ratio_delta = new_percent - old_percent;
257 	pair->diff.computed = true;
258 	return pair->diff.period_ratio_delta;
259 }
260 
261 static double compute_ratio(struct hist_entry *he, struct hist_entry *pair)
262 {
263 	double old_period = he->stat.period ?: 1;
264 	double new_period = pair->stat.period;
265 
266 	pair->diff.computed = true;
267 	pair->diff.period_ratio = new_period / old_period;
268 	return pair->diff.period_ratio;
269 }
270 
271 static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
272 {
273 	u64 old_period = he->stat.period;
274 	u64 new_period = pair->stat.period;
275 
276 	pair->diff.computed = true;
277 	pair->diff.wdiff = new_period * compute_wdiff_w2 -
278 			   old_period * compute_wdiff_w1;
279 
280 	return pair->diff.wdiff;
281 }
282 
283 static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
284 			 char *buf, size_t size)
285 {
286 	u64 he_total = he->hists->stats.total_period;
287 	u64 pair_total = pair->hists->stats.total_period;
288 
289 	if (symbol_conf.filter_relative) {
290 		he_total = he->hists->stats.total_non_filtered_period;
291 		pair_total = pair->hists->stats.total_non_filtered_period;
292 	}
293 	return scnprintf(buf, size,
294 			 "(%" PRIu64 " * 100 / %" PRIu64 ") - "
295 			 "(%" PRIu64 " * 100 / %" PRIu64 ")",
296 			 pair->stat.period, pair_total,
297 			 he->stat.period, he_total);
298 }
299 
300 static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
301 			 char *buf, size_t size)
302 {
303 	double old_period = he->stat.period;
304 	double new_period = pair->stat.period;
305 
306 	return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
307 }
308 
309 static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair,
310 			 char *buf, size_t size)
311 {
312 	u64 old_period = he->stat.period;
313 	u64 new_period = pair->stat.period;
314 
315 	return scnprintf(buf, size,
316 		  "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
317 		  new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
318 }
319 
320 static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
321 			   char *buf, size_t size)
322 {
323 	switch (compute) {
324 	case COMPUTE_DELTA:
325 	case COMPUTE_DELTA_ABS:
326 		return formula_delta(he, pair, buf, size);
327 	case COMPUTE_RATIO:
328 		return formula_ratio(he, pair, buf, size);
329 	case COMPUTE_WEIGHTED_DIFF:
330 		return formula_wdiff(he, pair, buf, size);
331 	default:
332 		BUG_ON(1);
333 	}
334 
335 	return -1;
336 }
337 
338 static int diff__process_sample_event(struct perf_tool *tool,
339 				      union perf_event *event,
340 				      struct perf_sample *sample,
341 				      struct perf_evsel *evsel,
342 				      struct machine *machine)
343 {
344 	struct perf_diff *pdiff = container_of(tool, struct perf_diff, tool);
345 	struct addr_location al;
346 	struct hists *hists = evsel__hists(evsel);
347 	int ret = -1;
348 
349 	if (perf_time__ranges_skip_sample(pdiff->ptime_range, pdiff->range_num,
350 					  sample->time)) {
351 		return 0;
352 	}
353 
354 	if (machine__resolve(machine, &al, sample) < 0) {
355 		pr_warning("problem processing %d event, skipping it.\n",
356 			   event->header.type);
357 		return -1;
358 	}
359 
360 	if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) {
361 		ret = 0;
362 		goto out_put;
363 	}
364 
365 	if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) {
366 		pr_warning("problem incrementing symbol period, skipping event\n");
367 		goto out_put;
368 	}
369 
370 	/*
371 	 * The total_period is updated here before going to the output
372 	 * tree since normally only the baseline hists will call
373 	 * hists__output_resort() and precompute needs the total
374 	 * period in order to sort entries by percentage delta.
375 	 */
376 	hists->stats.total_period += sample->period;
377 	if (!al.filtered)
378 		hists->stats.total_non_filtered_period += sample->period;
379 	ret = 0;
380 out_put:
381 	addr_location__put(&al);
382 	return ret;
383 }
384 
385 static struct perf_diff pdiff = {
386 	.tool = {
387 		.sample	= diff__process_sample_event,
388 		.mmap	= perf_event__process_mmap,
389 		.mmap2	= perf_event__process_mmap2,
390 		.comm	= perf_event__process_comm,
391 		.exit	= perf_event__process_exit,
392 		.fork	= perf_event__process_fork,
393 		.lost	= perf_event__process_lost,
394 		.namespaces = perf_event__process_namespaces,
395 		.ordered_events = true,
396 		.ordering_requires_timestamps = true,
397 	},
398 };
399 
400 static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
401 				      struct perf_evlist *evlist)
402 {
403 	struct perf_evsel *e;
404 
405 	evlist__for_each_entry(evlist, e) {
406 		if (perf_evsel__match2(evsel, e))
407 			return e;
408 	}
409 
410 	return NULL;
411 }
412 
413 static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
414 {
415 	struct perf_evsel *evsel;
416 
417 	evlist__for_each_entry(evlist, evsel) {
418 		struct hists *hists = evsel__hists(evsel);
419 
420 		hists__collapse_resort(hists, NULL);
421 	}
422 }
423 
424 static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt)
425 {
426 	struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt);
427 	void *ptr = dfmt - dfmt->idx;
428 	struct data__file *d = container_of(ptr, struct data__file, fmt);
429 
430 	return d;
431 }
432 
433 static struct hist_entry*
434 get_pair_data(struct hist_entry *he, struct data__file *d)
435 {
436 	if (hist_entry__has_pairs(he)) {
437 		struct hist_entry *pair;
438 
439 		list_for_each_entry(pair, &he->pairs.head, pairs.node)
440 			if (pair->hists == d->hists)
441 				return pair;
442 	}
443 
444 	return NULL;
445 }
446 
447 static struct hist_entry*
448 get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt)
449 {
450 	struct data__file *d = fmt_to_data_file(&dfmt->fmt);
451 
452 	return get_pair_data(he, d);
453 }
454 
455 static void hists__baseline_only(struct hists *hists)
456 {
457 	struct rb_root_cached *root;
458 	struct rb_node *next;
459 
460 	if (hists__has(hists, need_collapse))
461 		root = &hists->entries_collapsed;
462 	else
463 		root = hists->entries_in;
464 
465 	next = rb_first_cached(root);
466 	while (next != NULL) {
467 		struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
468 
469 		next = rb_next(&he->rb_node_in);
470 		if (!hist_entry__next_pair(he)) {
471 			rb_erase_cached(&he->rb_node_in, root);
472 			hist_entry__delete(he);
473 		}
474 	}
475 }
476 
477 static void hists__precompute(struct hists *hists)
478 {
479 	struct rb_root_cached *root;
480 	struct rb_node *next;
481 
482 	if (hists__has(hists, need_collapse))
483 		root = &hists->entries_collapsed;
484 	else
485 		root = hists->entries_in;
486 
487 	next = rb_first_cached(root);
488 	while (next != NULL) {
489 		struct hist_entry *he, *pair;
490 		struct data__file *d;
491 		int i;
492 
493 		he   = rb_entry(next, struct hist_entry, rb_node_in);
494 		next = rb_next(&he->rb_node_in);
495 
496 		data__for_each_file_new(i, d) {
497 			pair = get_pair_data(he, d);
498 			if (!pair)
499 				continue;
500 
501 			switch (compute) {
502 			case COMPUTE_DELTA:
503 			case COMPUTE_DELTA_ABS:
504 				compute_delta(he, pair);
505 				break;
506 			case COMPUTE_RATIO:
507 				compute_ratio(he, pair);
508 				break;
509 			case COMPUTE_WEIGHTED_DIFF:
510 				compute_wdiff(he, pair);
511 				break;
512 			default:
513 				BUG_ON(1);
514 			}
515 		}
516 	}
517 }
518 
519 static int64_t cmp_doubles(double l, double r)
520 {
521 	if (l > r)
522 		return -1;
523 	else if (l < r)
524 		return 1;
525 	else
526 		return 0;
527 }
528 
529 static int64_t
530 __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
531 			int c)
532 {
533 	switch (c) {
534 	case COMPUTE_DELTA:
535 	{
536 		double l = left->diff.period_ratio_delta;
537 		double r = right->diff.period_ratio_delta;
538 
539 		return cmp_doubles(l, r);
540 	}
541 	case COMPUTE_DELTA_ABS:
542 	{
543 		double l = fabs(left->diff.period_ratio_delta);
544 		double r = fabs(right->diff.period_ratio_delta);
545 
546 		return cmp_doubles(l, r);
547 	}
548 	case COMPUTE_RATIO:
549 	{
550 		double l = left->diff.period_ratio;
551 		double r = right->diff.period_ratio;
552 
553 		return cmp_doubles(l, r);
554 	}
555 	case COMPUTE_WEIGHTED_DIFF:
556 	{
557 		s64 l = left->diff.wdiff;
558 		s64 r = right->diff.wdiff;
559 
560 		return r - l;
561 	}
562 	default:
563 		BUG_ON(1);
564 	}
565 
566 	return 0;
567 }
568 
569 static int64_t
570 hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
571 			int c, int sort_idx)
572 {
573 	bool pairs_left  = hist_entry__has_pairs(left);
574 	bool pairs_right = hist_entry__has_pairs(right);
575 	struct hist_entry *p_right, *p_left;
576 
577 	if (!pairs_left && !pairs_right)
578 		return 0;
579 
580 	if (!pairs_left || !pairs_right)
581 		return pairs_left ? -1 : 1;
582 
583 	p_left  = get_pair_data(left,  &data__files[sort_idx]);
584 	p_right = get_pair_data(right, &data__files[sort_idx]);
585 
586 	if (!p_left && !p_right)
587 		return 0;
588 
589 	if (!p_left || !p_right)
590 		return p_left ? -1 : 1;
591 
592 	/*
593 	 * We have 2 entries of same kind, let's
594 	 * make the data comparison.
595 	 */
596 	return __hist_entry__cmp_compute(p_left, p_right, c);
597 }
598 
599 static int64_t
600 hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right,
601 			    int c, int sort_idx)
602 {
603 	struct hist_entry *p_right, *p_left;
604 
605 	p_left  = get_pair_data(left,  &data__files[sort_idx]);
606 	p_right = get_pair_data(right, &data__files[sort_idx]);
607 
608 	if (!p_left && !p_right)
609 		return 0;
610 
611 	if (!p_left || !p_right)
612 		return p_left ? -1 : 1;
613 
614 	if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) {
615 		/*
616 		 * The delta can be computed without the baseline, but
617 		 * others are not.  Put those entries which have no
618 		 * values below.
619 		 */
620 		if (left->dummy && right->dummy)
621 			return 0;
622 
623 		if (left->dummy || right->dummy)
624 			return left->dummy ? 1 : -1;
625 	}
626 
627 	return __hist_entry__cmp_compute(p_left, p_right, c);
628 }
629 
630 static int64_t
631 hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused,
632 		    struct hist_entry *left __maybe_unused,
633 		    struct hist_entry *right __maybe_unused)
634 {
635 	return 0;
636 }
637 
638 static int64_t
639 hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused,
640 			 struct hist_entry *left, struct hist_entry *right)
641 {
642 	if (left->stat.period == right->stat.period)
643 		return 0;
644 	return left->stat.period > right->stat.period ? 1 : -1;
645 }
646 
647 static int64_t
648 hist_entry__cmp_delta(struct perf_hpp_fmt *fmt,
649 		      struct hist_entry *left, struct hist_entry *right)
650 {
651 	struct data__file *d = fmt_to_data_file(fmt);
652 
653 	return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx);
654 }
655 
656 static int64_t
657 hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt,
658 		      struct hist_entry *left, struct hist_entry *right)
659 {
660 	struct data__file *d = fmt_to_data_file(fmt);
661 
662 	return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx);
663 }
664 
665 static int64_t
666 hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt,
667 		      struct hist_entry *left, struct hist_entry *right)
668 {
669 	struct data__file *d = fmt_to_data_file(fmt);
670 
671 	return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx);
672 }
673 
674 static int64_t
675 hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt,
676 		      struct hist_entry *left, struct hist_entry *right)
677 {
678 	struct data__file *d = fmt_to_data_file(fmt);
679 
680 	return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx);
681 }
682 
683 static int64_t
684 hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused,
685 			  struct hist_entry *left, struct hist_entry *right)
686 {
687 	return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA,
688 					   sort_compute);
689 }
690 
691 static int64_t
692 hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused,
693 			      struct hist_entry *left, struct hist_entry *right)
694 {
695 	return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS,
696 					   sort_compute);
697 }
698 
699 static int64_t
700 hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused,
701 			  struct hist_entry *left, struct hist_entry *right)
702 {
703 	return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO,
704 					   sort_compute);
705 }
706 
707 static int64_t
708 hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused,
709 			  struct hist_entry *left, struct hist_entry *right)
710 {
711 	return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF,
712 					   sort_compute);
713 }
714 
715 static void hists__process(struct hists *hists)
716 {
717 	if (show_baseline_only)
718 		hists__baseline_only(hists);
719 
720 	hists__precompute(hists);
721 	hists__output_resort(hists, NULL);
722 
723 	hists__fprintf(hists, !quiet, 0, 0, 0, stdout,
724 		       !symbol_conf.use_callchain);
725 }
726 
727 static void data__fprintf(void)
728 {
729 	struct data__file *d;
730 	int i;
731 
732 	fprintf(stdout, "# Data files:\n");
733 
734 	data__for_each_file(i, d)
735 		fprintf(stdout, "#  [%d] %s %s\n",
736 			d->idx, d->data.path,
737 			!d->idx ? "(Baseline)" : "");
738 
739 	fprintf(stdout, "#\n");
740 }
741 
742 static void data_process(void)
743 {
744 	struct perf_evlist *evlist_base = data__files[0].session->evlist;
745 	struct perf_evsel *evsel_base;
746 	bool first = true;
747 
748 	evlist__for_each_entry(evlist_base, evsel_base) {
749 		struct hists *hists_base = evsel__hists(evsel_base);
750 		struct data__file *d;
751 		int i;
752 
753 		data__for_each_file_new(i, d) {
754 			struct perf_evlist *evlist = d->session->evlist;
755 			struct perf_evsel *evsel;
756 			struct hists *hists;
757 
758 			evsel = evsel_match(evsel_base, evlist);
759 			if (!evsel)
760 				continue;
761 
762 			hists = evsel__hists(evsel);
763 			d->hists = hists;
764 
765 			hists__match(hists_base, hists);
766 
767 			if (!show_baseline_only)
768 				hists__link(hists_base, hists);
769 		}
770 
771 		if (!quiet) {
772 			fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
773 				perf_evsel__name(evsel_base));
774 		}
775 
776 		first = false;
777 
778 		if (verbose > 0 || ((data__files_cnt > 2) && !quiet))
779 			data__fprintf();
780 
781 		/* Don't sort callchain for perf diff */
782 		perf_evsel__reset_sample_bit(evsel_base, CALLCHAIN);
783 
784 		hists__process(hists_base);
785 	}
786 }
787 
788 static void data__free(struct data__file *d)
789 {
790 	int col;
791 
792 	for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
793 		struct diff_hpp_fmt *fmt = &d->fmt[col];
794 
795 		zfree(&fmt->header);
796 	}
797 }
798 
799 static int abstime_str_dup(char **pstr)
800 {
801 	char *str = NULL;
802 
803 	if (pdiff.time_str && strchr(pdiff.time_str, ':')) {
804 		str = strdup(pdiff.time_str);
805 		if (!str)
806 			return -ENOMEM;
807 	}
808 
809 	*pstr = str;
810 	return 0;
811 }
812 
813 static int parse_absolute_time(struct data__file *d, char **pstr)
814 {
815 	char *p = *pstr;
816 	int ret;
817 
818 	/*
819 	 * Absolute timestamp for one file has the format: a.b,c.d
820 	 * For multiple files, the format is: a.b,c.d:a.b,c.d
821 	 */
822 	p = strchr(*pstr, ':');
823 	if (p) {
824 		if (p == *pstr) {
825 			pr_err("Invalid time string\n");
826 			return -EINVAL;
827 		}
828 
829 		*p = 0;
830 		p++;
831 		if (*p == 0) {
832 			pr_err("Invalid time string\n");
833 			return -EINVAL;
834 		}
835 	}
836 
837 	ret = perf_time__parse_for_ranges(*pstr, d->session,
838 					  &pdiff.ptime_range,
839 					  &pdiff.range_size,
840 					  &pdiff.range_num);
841 	if (ret < 0)
842 		return ret;
843 
844 	if (!p || *p == 0)
845 		*pstr = NULL;
846 	else
847 		*pstr = p;
848 
849 	return ret;
850 }
851 
852 static int parse_percent_time(struct data__file *d)
853 {
854 	int ret;
855 
856 	ret = perf_time__parse_for_ranges(pdiff.time_str, d->session,
857 					  &pdiff.ptime_range,
858 					  &pdiff.range_size,
859 					  &pdiff.range_num);
860 	return ret;
861 }
862 
863 static int parse_time_str(struct data__file *d, char *abstime_ostr,
864 			   char **pabstime_tmp)
865 {
866 	int ret = 0;
867 
868 	if (abstime_ostr)
869 		ret = parse_absolute_time(d, pabstime_tmp);
870 	else if (pdiff.time_str)
871 		ret = parse_percent_time(d);
872 
873 	return ret;
874 }
875 
876 static int __cmd_diff(void)
877 {
878 	struct data__file *d;
879 	int ret, i;
880 	char *abstime_ostr, *abstime_tmp;
881 
882 	ret = abstime_str_dup(&abstime_ostr);
883 	if (ret)
884 		return ret;
885 
886 	abstime_tmp = abstime_ostr;
887 	ret = -EINVAL;
888 
889 	data__for_each_file(i, d) {
890 		d->session = perf_session__new(&d->data, false, &pdiff.tool);
891 		if (!d->session) {
892 			pr_err("Failed to open %s\n", d->data.path);
893 			ret = -1;
894 			goto out_delete;
895 		}
896 
897 		if (pdiff.time_str) {
898 			ret = parse_time_str(d, abstime_ostr, &abstime_tmp);
899 			if (ret < 0)
900 				goto out_delete;
901 		}
902 
903 		if (cpu_list) {
904 			ret = perf_session__cpu_bitmap(d->session, cpu_list,
905 						       cpu_bitmap);
906 			if (ret < 0)
907 				goto out_delete;
908 		}
909 
910 		ret = perf_session__process_events(d->session);
911 		if (ret) {
912 			pr_err("Failed to process %s\n", d->data.path);
913 			goto out_delete;
914 		}
915 
916 		perf_evlist__collapse_resort(d->session->evlist);
917 
918 		if (pdiff.ptime_range)
919 			zfree(&pdiff.ptime_range);
920 	}
921 
922 	data_process();
923 
924  out_delete:
925 	data__for_each_file(i, d) {
926 		perf_session__delete(d->session);
927 		data__free(d);
928 	}
929 
930 	free(data__files);
931 
932 	if (pdiff.ptime_range)
933 		zfree(&pdiff.ptime_range);
934 
935 	if (abstime_ostr)
936 		free(abstime_ostr);
937 
938 	return ret;
939 }
940 
941 static const char * const diff_usage[] = {
942 	"perf diff [<options>] [old_file] [new_file]",
943 	NULL,
944 };
945 
946 static const struct option options[] = {
947 	OPT_INCR('v', "verbose", &verbose,
948 		    "be more verbose (show symbol address, etc)"),
949 	OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"),
950 	OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
951 		    "Show only items with match in baseline"),
952 	OPT_CALLBACK('c', "compute", &compute,
953 		     "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs)",
954 		     "Entries differential computation selection",
955 		     setup_compute),
956 	OPT_BOOLEAN('p', "period", &show_period,
957 		    "Show period values."),
958 	OPT_BOOLEAN('F', "formula", &show_formula,
959 		    "Show formula."),
960 	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
961 		    "dump raw trace in ASCII"),
962 	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
963 	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
964 		   "file", "kallsyms pathname"),
965 	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
966 		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
967 	OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
968 		   "only consider symbols in these dsos"),
969 	OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
970 		   "only consider symbols in these comms"),
971 	OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
972 		   "only consider these symbols"),
973 	OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
974 		   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
975 		   " Please refer the man page for the complete list."),
976 	OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
977 		   "separator for columns, no spaces will be added between "
978 		   "columns '.' is reserved."),
979 	OPT_CALLBACK(0, "symfs", NULL, "directory",
980 		     "Look for files with symbols relative to this directory",
981 		     symbol__config_symfs),
982 	OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
983 	OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
984 		     "How to display percentage of filtered entries", parse_filter_percentage),
985 	OPT_STRING(0, "time", &pdiff.time_str, "str",
986 		   "Time span (time percent or absolute timestamp)"),
987 	OPT_STRING(0, "cpu", &cpu_list, "cpu", "list of cpus to profile"),
988 	OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
989 		   "only consider symbols in these pids"),
990 	OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
991 		   "only consider symbols in these tids"),
992 	OPT_END()
993 };
994 
995 static double baseline_percent(struct hist_entry *he)
996 {
997 	u64 total = hists__total_period(he->hists);
998 
999 	return 100.0 * he->stat.period / total;
1000 }
1001 
1002 static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
1003 			       struct perf_hpp *hpp, struct hist_entry *he)
1004 {
1005 	struct diff_hpp_fmt *dfmt =
1006 		container_of(fmt, struct diff_hpp_fmt, fmt);
1007 	double percent = baseline_percent(he);
1008 	char pfmt[20] = " ";
1009 
1010 	if (!he->dummy) {
1011 		scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1);
1012 		return percent_color_snprintf(hpp->buf, hpp->size,
1013 					      pfmt, percent);
1014 	} else
1015 		return scnprintf(hpp->buf, hpp->size, "%*s",
1016 				 dfmt->header_width, pfmt);
1017 }
1018 
1019 static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
1020 {
1021 	double percent = baseline_percent(he);
1022 	const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
1023 	int ret = 0;
1024 
1025 	if (!he->dummy)
1026 		ret = scnprintf(buf, size, fmt, percent);
1027 
1028 	return ret;
1029 }
1030 
1031 static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
1032 				struct perf_hpp *hpp, struct hist_entry *he,
1033 				int comparison_method)
1034 {
1035 	struct diff_hpp_fmt *dfmt =
1036 		container_of(fmt, struct diff_hpp_fmt, fmt);
1037 	struct hist_entry *pair = get_pair_fmt(he, dfmt);
1038 	double diff;
1039 	s64 wdiff;
1040 	char pfmt[20] = " ";
1041 
1042 	if (!pair)
1043 		goto no_print;
1044 
1045 	switch (comparison_method) {
1046 	case COMPUTE_DELTA:
1047 		if (pair->diff.computed)
1048 			diff = pair->diff.period_ratio_delta;
1049 		else
1050 			diff = compute_delta(he, pair);
1051 
1052 		scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1);
1053 		return percent_color_snprintf(hpp->buf, hpp->size,
1054 					pfmt, diff);
1055 	case COMPUTE_RATIO:
1056 		if (he->dummy)
1057 			goto dummy_print;
1058 		if (pair->diff.computed)
1059 			diff = pair->diff.period_ratio;
1060 		else
1061 			diff = compute_ratio(he, pair);
1062 
1063 		scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width);
1064 		return value_color_snprintf(hpp->buf, hpp->size,
1065 					pfmt, diff);
1066 	case COMPUTE_WEIGHTED_DIFF:
1067 		if (he->dummy)
1068 			goto dummy_print;
1069 		if (pair->diff.computed)
1070 			wdiff = pair->diff.wdiff;
1071 		else
1072 			wdiff = compute_wdiff(he, pair);
1073 
1074 		scnprintf(pfmt, 20, "%%14ld", dfmt->header_width);
1075 		return color_snprintf(hpp->buf, hpp->size,
1076 				get_percent_color(wdiff),
1077 				pfmt, wdiff);
1078 	default:
1079 		BUG_ON(1);
1080 	}
1081 dummy_print:
1082 	return scnprintf(hpp->buf, hpp->size, "%*s",
1083 			dfmt->header_width, "N/A");
1084 no_print:
1085 	return scnprintf(hpp->buf, hpp->size, "%*s",
1086 			dfmt->header_width, pfmt);
1087 }
1088 
1089 static int hpp__color_delta(struct perf_hpp_fmt *fmt,
1090 			struct perf_hpp *hpp, struct hist_entry *he)
1091 {
1092 	return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA);
1093 }
1094 
1095 static int hpp__color_ratio(struct perf_hpp_fmt *fmt,
1096 			struct perf_hpp *hpp, struct hist_entry *he)
1097 {
1098 	return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO);
1099 }
1100 
1101 static int hpp__color_wdiff(struct perf_hpp_fmt *fmt,
1102 			struct perf_hpp *hpp, struct hist_entry *he)
1103 {
1104 	return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
1105 }
1106 
1107 static void
1108 hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
1109 {
1110 	switch (idx) {
1111 	case PERF_HPP_DIFF__PERIOD_BASELINE:
1112 		scnprintf(buf, size, "%" PRIu64, he->stat.period);
1113 		break;
1114 
1115 	default:
1116 		break;
1117 	}
1118 }
1119 
1120 static void
1121 hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
1122 		int idx, char *buf, size_t size)
1123 {
1124 	double diff;
1125 	double ratio;
1126 	s64 wdiff;
1127 
1128 	switch (idx) {
1129 	case PERF_HPP_DIFF__DELTA:
1130 	case PERF_HPP_DIFF__DELTA_ABS:
1131 		if (pair->diff.computed)
1132 			diff = pair->diff.period_ratio_delta;
1133 		else
1134 			diff = compute_delta(he, pair);
1135 
1136 		scnprintf(buf, size, "%+4.2F%%", diff);
1137 		break;
1138 
1139 	case PERF_HPP_DIFF__RATIO:
1140 		/* No point for ratio number if we are dummy.. */
1141 		if (he->dummy) {
1142 			scnprintf(buf, size, "N/A");
1143 			break;
1144 		}
1145 
1146 		if (pair->diff.computed)
1147 			ratio = pair->diff.period_ratio;
1148 		else
1149 			ratio = compute_ratio(he, pair);
1150 
1151 		if (ratio > 0.0)
1152 			scnprintf(buf, size, "%14.6F", ratio);
1153 		break;
1154 
1155 	case PERF_HPP_DIFF__WEIGHTED_DIFF:
1156 		/* No point for wdiff number if we are dummy.. */
1157 		if (he->dummy) {
1158 			scnprintf(buf, size, "N/A");
1159 			break;
1160 		}
1161 
1162 		if (pair->diff.computed)
1163 			wdiff = pair->diff.wdiff;
1164 		else
1165 			wdiff = compute_wdiff(he, pair);
1166 
1167 		if (wdiff != 0)
1168 			scnprintf(buf, size, "%14ld", wdiff);
1169 		break;
1170 
1171 	case PERF_HPP_DIFF__FORMULA:
1172 		formula_fprintf(he, pair, buf, size);
1173 		break;
1174 
1175 	case PERF_HPP_DIFF__PERIOD:
1176 		scnprintf(buf, size, "%" PRIu64, pair->stat.period);
1177 		break;
1178 
1179 	default:
1180 		BUG_ON(1);
1181 	};
1182 }
1183 
1184 static void
1185 __hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt,
1186 		    char *buf, size_t size)
1187 {
1188 	struct hist_entry *pair = get_pair_fmt(he, dfmt);
1189 	int idx = dfmt->idx;
1190 
1191 	/* baseline is special */
1192 	if (idx == PERF_HPP_DIFF__BASELINE)
1193 		hpp__entry_baseline(he, buf, size);
1194 	else {
1195 		if (pair)
1196 			hpp__entry_pair(he, pair, idx, buf, size);
1197 		else
1198 			hpp__entry_unpair(he, idx, buf, size);
1199 	}
1200 }
1201 
1202 static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,
1203 			     struct hist_entry *he)
1204 {
1205 	struct diff_hpp_fmt *dfmt =
1206 		container_of(_fmt, struct diff_hpp_fmt, fmt);
1207 	char buf[MAX_COL_WIDTH] = " ";
1208 
1209 	__hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH);
1210 
1211 	if (symbol_conf.field_sep)
1212 		return scnprintf(hpp->buf, hpp->size, "%s", buf);
1213 	else
1214 		return scnprintf(hpp->buf, hpp->size, "%*s",
1215 				 dfmt->header_width, buf);
1216 }
1217 
1218 static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1219 		       struct hists *hists __maybe_unused,
1220 		       int line __maybe_unused,
1221 		       int *span __maybe_unused)
1222 {
1223 	struct diff_hpp_fmt *dfmt =
1224 		container_of(fmt, struct diff_hpp_fmt, fmt);
1225 
1226 	BUG_ON(!dfmt->header);
1227 	return scnprintf(hpp->buf, hpp->size, dfmt->header);
1228 }
1229 
1230 static int hpp__width(struct perf_hpp_fmt *fmt,
1231 		      struct perf_hpp *hpp __maybe_unused,
1232 		      struct hists *hists __maybe_unused)
1233 {
1234 	struct diff_hpp_fmt *dfmt =
1235 		container_of(fmt, struct diff_hpp_fmt, fmt);
1236 
1237 	BUG_ON(dfmt->header_width <= 0);
1238 	return dfmt->header_width;
1239 }
1240 
1241 static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt)
1242 {
1243 #define MAX_HEADER_NAME 100
1244 	char buf_indent[MAX_HEADER_NAME];
1245 	char buf[MAX_HEADER_NAME];
1246 	const char *header = NULL;
1247 	int width = 0;
1248 
1249 	BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX);
1250 	header = columns[dfmt->idx].name;
1251 	width  = columns[dfmt->idx].width;
1252 
1253 	/* Only our defined HPP fmts should appear here. */
1254 	BUG_ON(!header);
1255 
1256 	if (data__files_cnt > 2)
1257 		scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx);
1258 
1259 #define NAME (data__files_cnt > 2 ? buf : header)
1260 	dfmt->header_width = width;
1261 	width = (int) strlen(NAME);
1262 	if (dfmt->header_width < width)
1263 		dfmt->header_width = width;
1264 
1265 	scnprintf(buf_indent, MAX_HEADER_NAME, "%*s",
1266 		  dfmt->header_width, NAME);
1267 
1268 	dfmt->header = strdup(buf_indent);
1269 #undef MAX_HEADER_NAME
1270 #undef NAME
1271 }
1272 
1273 static void data__hpp_register(struct data__file *d, int idx)
1274 {
1275 	struct diff_hpp_fmt *dfmt = &d->fmt[idx];
1276 	struct perf_hpp_fmt *fmt = &dfmt->fmt;
1277 
1278 	dfmt->idx = idx;
1279 
1280 	fmt->header = hpp__header;
1281 	fmt->width  = hpp__width;
1282 	fmt->entry  = hpp__entry_global;
1283 	fmt->cmp    = hist_entry__cmp_nop;
1284 	fmt->collapse = hist_entry__cmp_nop;
1285 
1286 	/* TODO more colors */
1287 	switch (idx) {
1288 	case PERF_HPP_DIFF__BASELINE:
1289 		fmt->color = hpp__color_baseline;
1290 		fmt->sort  = hist_entry__cmp_baseline;
1291 		break;
1292 	case PERF_HPP_DIFF__DELTA:
1293 		fmt->color = hpp__color_delta;
1294 		fmt->sort  = hist_entry__cmp_delta;
1295 		break;
1296 	case PERF_HPP_DIFF__RATIO:
1297 		fmt->color = hpp__color_ratio;
1298 		fmt->sort  = hist_entry__cmp_ratio;
1299 		break;
1300 	case PERF_HPP_DIFF__WEIGHTED_DIFF:
1301 		fmt->color = hpp__color_wdiff;
1302 		fmt->sort  = hist_entry__cmp_wdiff;
1303 		break;
1304 	case PERF_HPP_DIFF__DELTA_ABS:
1305 		fmt->color = hpp__color_delta;
1306 		fmt->sort  = hist_entry__cmp_delta_abs;
1307 		break;
1308 	default:
1309 		fmt->sort  = hist_entry__cmp_nop;
1310 		break;
1311 	}
1312 
1313 	init_header(d, dfmt);
1314 	perf_hpp__column_register(fmt);
1315 	perf_hpp__register_sort_field(fmt);
1316 }
1317 
1318 static int ui_init(void)
1319 {
1320 	struct data__file *d;
1321 	struct perf_hpp_fmt *fmt;
1322 	int i;
1323 
1324 	data__for_each_file(i, d) {
1325 
1326 		/*
1327 		 * Baseline or compute realted columns:
1328 		 *
1329 		 *   PERF_HPP_DIFF__BASELINE
1330 		 *   PERF_HPP_DIFF__DELTA
1331 		 *   PERF_HPP_DIFF__RATIO
1332 		 *   PERF_HPP_DIFF__WEIGHTED_DIFF
1333 		 */
1334 		data__hpp_register(d, i ? compute_2_hpp[compute] :
1335 					  PERF_HPP_DIFF__BASELINE);
1336 
1337 		/*
1338 		 * And the rest:
1339 		 *
1340 		 * PERF_HPP_DIFF__FORMULA
1341 		 * PERF_HPP_DIFF__PERIOD
1342 		 * PERF_HPP_DIFF__PERIOD_BASELINE
1343 		 */
1344 		if (show_formula && i)
1345 			data__hpp_register(d, PERF_HPP_DIFF__FORMULA);
1346 
1347 		if (show_period)
1348 			data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
1349 						  PERF_HPP_DIFF__PERIOD_BASELINE);
1350 	}
1351 
1352 	if (!sort_compute)
1353 		return 0;
1354 
1355 	/*
1356 	 * Prepend an fmt to sort on columns at 'sort_compute' first.
1357 	 * This fmt is added only to the sort list but not to the
1358 	 * output fields list.
1359 	 *
1360 	 * Note that this column (data) can be compared twice - one
1361 	 * for this 'sort_compute' fmt and another for the normal
1362 	 * diff_hpp_fmt.  But it shouldn't a problem as most entries
1363 	 * will be sorted out by first try or baseline and comparing
1364 	 * is not a costly operation.
1365 	 */
1366 	fmt = zalloc(sizeof(*fmt));
1367 	if (fmt == NULL) {
1368 		pr_err("Memory allocation failed\n");
1369 		return -1;
1370 	}
1371 
1372 	fmt->cmp      = hist_entry__cmp_nop;
1373 	fmt->collapse = hist_entry__cmp_nop;
1374 
1375 	switch (compute) {
1376 	case COMPUTE_DELTA:
1377 		fmt->sort = hist_entry__cmp_delta_idx;
1378 		break;
1379 	case COMPUTE_RATIO:
1380 		fmt->sort = hist_entry__cmp_ratio_idx;
1381 		break;
1382 	case COMPUTE_WEIGHTED_DIFF:
1383 		fmt->sort = hist_entry__cmp_wdiff_idx;
1384 		break;
1385 	case COMPUTE_DELTA_ABS:
1386 		fmt->sort = hist_entry__cmp_delta_abs_idx;
1387 		break;
1388 	default:
1389 		BUG_ON(1);
1390 	}
1391 
1392 	perf_hpp__prepend_sort_field(fmt);
1393 	return 0;
1394 }
1395 
1396 static int data_init(int argc, const char **argv)
1397 {
1398 	struct data__file *d;
1399 	static const char *defaults[] = {
1400 		"perf.data.old",
1401 		"perf.data",
1402 	};
1403 	bool use_default = true;
1404 	int i;
1405 
1406 	data__files_cnt = 2;
1407 
1408 	if (argc) {
1409 		if (argc == 1)
1410 			defaults[1] = argv[0];
1411 		else {
1412 			data__files_cnt = argc;
1413 			use_default = false;
1414 		}
1415 	} else if (perf_guest) {
1416 		defaults[0] = "perf.data.host";
1417 		defaults[1] = "perf.data.guest";
1418 	}
1419 
1420 	if (sort_compute >= (unsigned int) data__files_cnt) {
1421 		pr_err("Order option out of limit.\n");
1422 		return -EINVAL;
1423 	}
1424 
1425 	data__files = zalloc(sizeof(*data__files) * data__files_cnt);
1426 	if (!data__files)
1427 		return -ENOMEM;
1428 
1429 	data__for_each_file(i, d) {
1430 		struct perf_data *data = &d->data;
1431 
1432 		data->path  = use_default ? defaults[i] : argv[i];
1433 		data->mode  = PERF_DATA_MODE_READ,
1434 		data->force = force,
1435 
1436 		d->idx  = i;
1437 	}
1438 
1439 	return 0;
1440 }
1441 
1442 static int diff__config(const char *var, const char *value,
1443 			void *cb __maybe_unused)
1444 {
1445 	if (!strcmp(var, "diff.order")) {
1446 		int ret;
1447 		if (perf_config_int(&ret, var, value) < 0)
1448 			return -1;
1449 		sort_compute = ret;
1450 		return 0;
1451 	}
1452 	if (!strcmp(var, "diff.compute")) {
1453 		if (!strcmp(value, "delta")) {
1454 			compute = COMPUTE_DELTA;
1455 		} else if (!strcmp(value, "delta-abs")) {
1456 			compute = COMPUTE_DELTA_ABS;
1457 		} else if (!strcmp(value, "ratio")) {
1458 			compute = COMPUTE_RATIO;
1459 		} else if (!strcmp(value, "wdiff")) {
1460 			compute = COMPUTE_WEIGHTED_DIFF;
1461 		} else {
1462 			pr_err("Invalid compute method: %s\n", value);
1463 			return -1;
1464 		}
1465 	}
1466 
1467 	return 0;
1468 }
1469 
1470 int cmd_diff(int argc, const char **argv)
1471 {
1472 	int ret = hists__init();
1473 
1474 	if (ret < 0)
1475 		return ret;
1476 
1477 	perf_config(diff__config, NULL);
1478 
1479 	argc = parse_options(argc, argv, options, diff_usage, 0);
1480 
1481 	if (quiet)
1482 		perf_quiet_option();
1483 
1484 	if (symbol__init(NULL) < 0)
1485 		return -1;
1486 
1487 	if (data_init(argc, argv) < 0)
1488 		return -1;
1489 
1490 	if (ui_init() < 0)
1491 		return -1;
1492 
1493 	sort__mode = SORT_MODE__DIFF;
1494 
1495 	if (setup_sorting(NULL) < 0)
1496 		usage_with_options(diff_usage, options);
1497 
1498 	setup_pager();
1499 
1500 	sort__setup_elide(NULL);
1501 
1502 	return __cmd_diff();
1503 }
1504