blob: 781c9e60bd213b35c792f4eafc4c0a67c47e66b6 [file] [log] [blame]
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -02001/*
2 * builtin-diff.c
3 *
4 * Builtin diff command: Analyze two perf.data input files, look up and read
5 * DSOs and symbol information, sort them and produce a diff.
6 */
7#include "builtin.h"
8
9#include "util/debug.h"
10#include "util/event.h"
11#include "util/hist.h"
Arnaldo Carvalho de Melo743eb862011-11-28 07:56:39 -020012#include "util/evsel.h"
Jiri Olsa863e4512012-09-06 17:46:55 +020013#include "util/evlist.h"
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -020014#include "util/session.h"
Arnaldo Carvalho de Melo45694aa2011-11-28 08:30:20 -020015#include "util/tool.h"
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -020016#include "util/sort.h"
17#include "util/symbol.h"
18#include "util/util.h"
Jiri Olsaf5fc14122013-10-15 16:27:32 +020019#include "util/data.h"
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -020020
21#include <stdlib.h>
Jiri Olsa345dc0b2013-02-03 20:08:34 +010022#include <math.h>
23
24/* Diff command specific HPP columns. */
25enum {
26 PERF_HPP_DIFF__BASELINE,
27 PERF_HPP_DIFF__PERIOD,
28 PERF_HPP_DIFF__PERIOD_BASELINE,
29 PERF_HPP_DIFF__DELTA,
30 PERF_HPP_DIFF__RATIO,
31 PERF_HPP_DIFF__WEIGHTED_DIFF,
32 PERF_HPP_DIFF__FORMULA,
Namhyung Kima1668c22017-02-10 16:36:11 +090033 PERF_HPP_DIFF__DELTA_ABS,
Jiri Olsa345dc0b2013-02-03 20:08:34 +010034
35 PERF_HPP_DIFF__MAX_INDEX
36};
37
38struct diff_hpp_fmt {
39 struct perf_hpp_fmt fmt;
40 int idx;
41 char *header;
42 int header_width;
43};
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -020044
Jiri Olsaec308422013-03-25 00:02:01 +010045struct data__file {
46 struct perf_session *session;
Jiri Olsaf5fc14122013-10-15 16:27:32 +020047 struct perf_data_file file;
Jiri Olsaec308422013-03-25 00:02:01 +010048 int idx;
Jiri Olsa22aeb7f2012-12-01 22:00:00 +010049 struct hists *hists;
Jiri Olsac818b492012-12-01 21:57:04 +010050 struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX];
Jiri Olsaec308422013-03-25 00:02:01 +010051};
52
53static struct data__file *data__files;
54static int data__files_cnt;
55
56#define data__for_each_file_start(i, d, s) \
57 for (i = s, d = &data__files[s]; \
58 i < data__files_cnt; \
59 i++, d = &data__files[i])
60
61#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
Jiri Olsa22aeb7f2012-12-01 22:00:00 +010062#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
Jiri Olsaec308422013-03-25 00:02:01 +010063
Jiri Olsaec308422013-03-25 00:02:01 +010064static bool force;
Jiri Olsa61949b22012-10-05 16:44:44 +020065static bool show_period;
Jiri Olsaed279da2012-10-05 16:44:45 +020066static bool show_formula;
Jiri Olsaa06d1432012-10-05 16:44:40 +020067static bool show_baseline_only;
Jiri Olsa5f3f8d32012-11-25 23:10:20 +010068static unsigned int sort_compute;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -020069
Jiri Olsa81d5f952012-10-05 16:44:43 +020070static s64 compute_wdiff_w1;
71static s64 compute_wdiff_w2;
72
Jiri Olsa7aaf6b32012-10-05 16:44:41 +020073enum {
74 COMPUTE_DELTA,
75 COMPUTE_RATIO,
Jiri Olsa81d5f952012-10-05 16:44:43 +020076 COMPUTE_WEIGHTED_DIFF,
Namhyung Kima1668c22017-02-10 16:36:11 +090077 COMPUTE_DELTA_ABS,
Jiri Olsa7aaf6b32012-10-05 16:44:41 +020078 COMPUTE_MAX,
79};
80
81const char *compute_names[COMPUTE_MAX] = {
82 [COMPUTE_DELTA] = "delta",
Namhyung Kima1668c22017-02-10 16:36:11 +090083 [COMPUTE_DELTA_ABS] = "delta-abs",
Jiri Olsa7aaf6b32012-10-05 16:44:41 +020084 [COMPUTE_RATIO] = "ratio",
Jiri Olsa81d5f952012-10-05 16:44:43 +020085 [COMPUTE_WEIGHTED_DIFF] = "wdiff",
Jiri Olsa7aaf6b32012-10-05 16:44:41 +020086};
87
88static int compute;
89
Jiri Olsa345dc0b2013-02-03 20:08:34 +010090static int compute_2_hpp[COMPUTE_MAX] = {
91 [COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA,
Namhyung Kima1668c22017-02-10 16:36:11 +090092 [COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS,
Jiri Olsa345dc0b2013-02-03 20:08:34 +010093 [COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
94 [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
95};
96
97#define MAX_COL_WIDTH 70
98
99static struct header_column {
100 const char *name;
101 int width;
102} columns[PERF_HPP_DIFF__MAX_INDEX] = {
103 [PERF_HPP_DIFF__BASELINE] = {
104 .name = "Baseline",
105 },
106 [PERF_HPP_DIFF__PERIOD] = {
107 .name = "Period",
108 .width = 14,
109 },
110 [PERF_HPP_DIFF__PERIOD_BASELINE] = {
111 .name = "Base period",
112 .width = 14,
113 },
114 [PERF_HPP_DIFF__DELTA] = {
115 .name = "Delta",
116 .width = 7,
117 },
Namhyung Kima1668c22017-02-10 16:36:11 +0900118 [PERF_HPP_DIFF__DELTA_ABS] = {
119 .name = "Delta Abs",
120 .width = 7,
121 },
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100122 [PERF_HPP_DIFF__RATIO] = {
123 .name = "Ratio",
124 .width = 14,
125 },
126 [PERF_HPP_DIFF__WEIGHTED_DIFF] = {
127 .name = "Weighted diff",
128 .width = 14,
129 },
130 [PERF_HPP_DIFF__FORMULA] = {
131 .name = "Formula",
132 .width = MAX_COL_WIDTH,
133 }
134};
135
Jiri Olsa81d5f952012-10-05 16:44:43 +0200136static int setup_compute_opt_wdiff(char *opt)
137{
138 char *w1_str = opt;
139 char *w2_str;
140
141 int ret = -EINVAL;
142
143 if (!opt)
144 goto out;
145
146 w2_str = strchr(opt, ',');
147 if (!w2_str)
148 goto out;
149
150 *w2_str++ = 0x0;
151 if (!*w2_str)
152 goto out;
153
154 compute_wdiff_w1 = strtol(w1_str, NULL, 10);
155 compute_wdiff_w2 = strtol(w2_str, NULL, 10);
156
157 if (!compute_wdiff_w1 || !compute_wdiff_w2)
158 goto out;
159
160 pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
161 compute_wdiff_w1, compute_wdiff_w2);
162
163 ret = 0;
164
165 out:
166 if (ret)
167 pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
168
169 return ret;
170}
171
172static int setup_compute_opt(char *opt)
173{
174 if (compute == COMPUTE_WEIGHTED_DIFF)
175 return setup_compute_opt_wdiff(opt);
176
177 if (opt) {
178 pr_err("Failed: extra option specified '%s'", opt);
179 return -EINVAL;
180 }
181
182 return 0;
183}
184
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200185static int setup_compute(const struct option *opt, const char *str,
186 int unset __maybe_unused)
187{
188 int *cp = (int *) opt->value;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200189 char *cstr = (char *) str;
190 char buf[50];
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200191 unsigned i;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200192 char *option;
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200193
194 if (!str) {
195 *cp = COMPUTE_DELTA;
196 return 0;
197 }
198
Jiri Olsa81d5f952012-10-05 16:44:43 +0200199 option = strchr(str, ':');
200 if (option) {
201 unsigned len = option++ - str;
202
203 /*
204 * The str data are not writeable, so we need
205 * to use another buffer.
206 */
207
208 /* No option value is longer. */
209 if (len >= sizeof(buf))
210 return -EINVAL;
211
212 strncpy(buf, str, len);
213 buf[len] = 0x0;
214 cstr = buf;
215 }
216
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200217 for (i = 0; i < COMPUTE_MAX; i++)
Jiri Olsa81d5f952012-10-05 16:44:43 +0200218 if (!strcmp(cstr, compute_names[i])) {
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200219 *cp = i;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200220 return setup_compute_opt(option);
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200221 }
222
223 pr_err("Failed: '%s' is not computation method "
Jiri Olsa81d5f952012-10-05 16:44:43 +0200224 "(use 'delta','ratio' or 'wdiff')\n", str);
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200225 return -EINVAL;
226}
227
Jiri Olsaef358e62012-10-21 23:31:51 +0200228static double period_percent(struct hist_entry *he, u64 period)
Jiri Olsa96c47f12012-10-05 16:44:42 +0200229{
Namhyung Kim8810f6c2014-02-07 12:06:07 +0900230 u64 total = hists__total_period(he->hists);
231
Jiri Olsa96c47f12012-10-05 16:44:42 +0200232 return (period * 100.0) / total;
233}
234
Jiri Olsaef358e62012-10-21 23:31:51 +0200235static double compute_delta(struct hist_entry *he, struct hist_entry *pair)
Jiri Olsa96c47f12012-10-05 16:44:42 +0200236{
Jiri Olsaef358e62012-10-21 23:31:51 +0200237 double old_percent = period_percent(he, he->stat.period);
238 double new_percent = period_percent(pair, pair->stat.period);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200239
Jiri Olsa9af303e2012-12-01 21:15:40 +0100240 pair->diff.period_ratio_delta = new_percent - old_percent;
241 pair->diff.computed = true;
242 return pair->diff.period_ratio_delta;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200243}
244
Jiri Olsaef358e62012-10-21 23:31:51 +0200245static double compute_ratio(struct hist_entry *he, struct hist_entry *pair)
Jiri Olsa96c47f12012-10-05 16:44:42 +0200246{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100247 double old_period = he->stat.period ?: 1;
248 double new_period = pair->stat.period;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200249
Jiri Olsa9af303e2012-12-01 21:15:40 +0100250 pair->diff.computed = true;
251 pair->diff.period_ratio = new_period / old_period;
252 return pair->diff.period_ratio;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200253}
254
Jiri Olsaef358e62012-10-21 23:31:51 +0200255static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
Jiri Olsa81d5f952012-10-05 16:44:43 +0200256{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100257 u64 old_period = he->stat.period;
258 u64 new_period = pair->stat.period;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200259
Jiri Olsa9af303e2012-12-01 21:15:40 +0100260 pair->diff.computed = true;
261 pair->diff.wdiff = new_period * compute_wdiff_w2 -
262 old_period * compute_wdiff_w1;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200263
Jiri Olsa9af303e2012-12-01 21:15:40 +0100264 return pair->diff.wdiff;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200265}
266
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100267static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
268 char *buf, size_t size)
Jiri Olsaed279da2012-10-05 16:44:45 +0200269{
Namhyung Kim8810f6c2014-02-07 12:06:07 +0900270 u64 he_total = he->hists->stats.total_period;
271 u64 pair_total = pair->hists->stats.total_period;
272
273 if (symbol_conf.filter_relative) {
274 he_total = he->hists->stats.total_non_filtered_period;
275 pair_total = pair->hists->stats.total_non_filtered_period;
276 }
Jiri Olsaed279da2012-10-05 16:44:45 +0200277 return scnprintf(buf, size,
278 "(%" PRIu64 " * 100 / %" PRIu64 ") - "
279 "(%" PRIu64 " * 100 / %" PRIu64 ")",
Namhyung Kim8810f6c2014-02-07 12:06:07 +0900280 pair->stat.period, pair_total,
281 he->stat.period, he_total);
Jiri Olsaed279da2012-10-05 16:44:45 +0200282}
283
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100284static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
285 char *buf, size_t size)
Jiri Olsaed279da2012-10-05 16:44:45 +0200286{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100287 double old_period = he->stat.period;
288 double new_period = pair->stat.period;
Jiri Olsaed279da2012-10-05 16:44:45 +0200289
290 return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
291}
292
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100293static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair,
294 char *buf, size_t size)
Jiri Olsaed279da2012-10-05 16:44:45 +0200295{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100296 u64 old_period = he->stat.period;
297 u64 new_period = pair->stat.period;
Jiri Olsaed279da2012-10-05 16:44:45 +0200298
299 return scnprintf(buf, size,
300 "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
301 new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
302}
303
Jiri Olsaef358e62012-10-21 23:31:51 +0200304static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
305 char *buf, size_t size)
Jiri Olsaed279da2012-10-05 16:44:45 +0200306{
307 switch (compute) {
308 case COMPUTE_DELTA:
Namhyung Kima1668c22017-02-10 16:36:11 +0900309 case COMPUTE_DELTA_ABS:
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100310 return formula_delta(he, pair, buf, size);
Jiri Olsaed279da2012-10-05 16:44:45 +0200311 case COMPUTE_RATIO:
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100312 return formula_ratio(he, pair, buf, size);
Jiri Olsaed279da2012-10-05 16:44:45 +0200313 case COMPUTE_WEIGHTED_DIFF:
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100314 return formula_wdiff(he, pair, buf, size);
Jiri Olsaed279da2012-10-05 16:44:45 +0200315 default:
316 BUG_ON(1);
317 }
318
319 return -1;
320}
321
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300322static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
Arnaldo Carvalho de Melod20deb62011-11-25 08:19:45 -0200323 union perf_event *event,
Arnaldo Carvalho de Melo8d50e5b2011-01-29 13:02:00 -0200324 struct perf_sample *sample,
Jiri Olsa863e4512012-09-06 17:46:55 +0200325 struct perf_evsel *evsel,
Arnaldo Carvalho de Melo743eb862011-11-28 07:56:39 -0200326 struct machine *machine)
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200327{
328 struct addr_location al;
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300329 struct hists *hists = evsel__hists(evsel);
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -0300330 int ret = -1;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200331
Arnaldo Carvalho de Melobb3eb562016-03-22 18:39:09 -0300332 if (machine__resolve(machine, &al, sample) < 0) {
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200333 pr_warning("problem processing %d event, skipping it.\n",
334 event->header.type);
335 return -1;
336 }
337
Jiri Olsa0102ef32016-06-14 20:19:21 +0200338 if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) {
Arnaldo Carvalho de Meloc82ee822010-05-14 14:19:35 -0300339 pr_warning("problem incrementing symbol period, skipping event\n");
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -0300340 goto out_put;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200341 }
342
Namhyung Kim820bc812014-04-22 11:44:21 +0900343 /*
344 * The total_period is updated here before going to the output
345 * tree since normally only the baseline hists will call
346 * hists__output_resort() and precompute needs the total
347 * period in order to sort entries by percentage delta.
348 */
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300349 hists->stats.total_period += sample->period;
Namhyung Kim820bc812014-04-22 11:44:21 +0900350 if (!al.filtered)
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300351 hists->stats.total_non_filtered_period += sample->period;
Arnaldo Carvalho de Melob91fc392015-04-06 20:43:22 -0300352 ret = 0;
353out_put:
354 addr_location__put(&al);
355 return ret;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200356}
357
Jiri Olsa863e4512012-09-06 17:46:55 +0200358static struct perf_tool tool = {
359 .sample = diff__process_sample_event,
360 .mmap = perf_event__process_mmap,
Kan Liang68ca9d62014-11-18 11:38:18 -0500361 .mmap2 = perf_event__process_mmap2,
Jiri Olsa863e4512012-09-06 17:46:55 +0200362 .comm = perf_event__process_comm,
Arnaldo Carvalho de Melof62d3f02012-10-06 15:44:59 -0300363 .exit = perf_event__process_exit,
364 .fork = perf_event__process_fork,
Jiri Olsa863e4512012-09-06 17:46:55 +0200365 .lost = perf_event__process_lost,
Jiri Olsa0a8cb852014-07-06 14:18:21 +0200366 .ordered_events = true,
Jiri Olsa863e4512012-09-06 17:46:55 +0200367 .ordering_requires_timestamps = true,
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200368};
369
Jiri Olsa863e4512012-09-06 17:46:55 +0200370static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
371 struct perf_evlist *evlist)
372{
373 struct perf_evsel *e;
374
Arnaldo Carvalho de Meloe5cadb92016-06-23 11:26:15 -0300375 evlist__for_each_entry(evlist, e) {
Jiri Olsa863e4512012-09-06 17:46:55 +0200376 if (perf_evsel__match2(evsel, e))
377 return e;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -0300378 }
Jiri Olsa863e4512012-09-06 17:46:55 +0200379
380 return NULL;
381}
382
Namhyung Kimce74f602012-12-10 17:29:55 +0900383static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
Jiri Olsadd464342012-10-04 21:49:36 +0900384{
385 struct perf_evsel *evsel;
386
Arnaldo Carvalho de Meloe5cadb92016-06-23 11:26:15 -0300387 evlist__for_each_entry(evlist, evsel) {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300388 struct hists *hists = evsel__hists(evsel);
Jiri Olsadd464342012-10-04 21:49:36 +0900389
Namhyung Kimc1fb5652013-10-11 14:15:38 +0900390 hists__collapse_resort(hists, NULL);
Jiri Olsadd464342012-10-04 21:49:36 +0900391 }
392}
393
Namhyung Kimff21cef2015-01-08 09:45:45 +0900394static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt)
395{
396 struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt);
397 void *ptr = dfmt - dfmt->idx;
398 struct data__file *d = container_of(ptr, struct data__file, fmt);
399
400 return d;
401}
402
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100403static struct hist_entry*
404get_pair_data(struct hist_entry *he, struct data__file *d)
405{
406 if (hist_entry__has_pairs(he)) {
407 struct hist_entry *pair;
408
409 list_for_each_entry(pair, &he->pairs.head, pairs.node)
410 if (pair->hists == d->hists)
411 return pair;
412 }
413
414 return NULL;
415}
416
417static struct hist_entry*
418get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt)
419{
Namhyung Kimff21cef2015-01-08 09:45:45 +0900420 struct data__file *d = fmt_to_data_file(&dfmt->fmt);
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100421
422 return get_pair_data(he, d);
423}
424
Jiri Olsaa06d1432012-10-05 16:44:40 +0200425static void hists__baseline_only(struct hists *hists)
426{
Namhyung Kimce74f602012-12-10 17:29:55 +0900427 struct rb_root *root;
428 struct rb_node *next;
Jiri Olsaa06d1432012-10-05 16:44:40 +0200429
Jiri Olsa52225032016-05-03 13:54:42 +0200430 if (hists__has(hists, need_collapse))
Namhyung Kimce74f602012-12-10 17:29:55 +0900431 root = &hists->entries_collapsed;
432 else
433 root = hists->entries_in;
434
435 next = rb_first(root);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200436 while (next != NULL) {
Namhyung Kimce74f602012-12-10 17:29:55 +0900437 struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200438
Namhyung Kimce74f602012-12-10 17:29:55 +0900439 next = rb_next(&he->rb_node_in);
Arnaldo Carvalho de Melob821c732012-10-25 14:42:45 -0200440 if (!hist_entry__next_pair(he)) {
Namhyung Kimce74f602012-12-10 17:29:55 +0900441 rb_erase(&he->rb_node_in, root);
Arnaldo Carvalho de Melo6733d1b2014-12-19 12:31:40 -0300442 hist_entry__delete(he);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200443 }
444 }
445}
446
Jiri Olsa96c47f12012-10-05 16:44:42 +0200447static void hists__precompute(struct hists *hists)
448{
Jiri Olsa367c53c2012-12-13 14:08:59 +0100449 struct rb_root *root;
450 struct rb_node *next;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200451
Jiri Olsa52225032016-05-03 13:54:42 +0200452 if (hists__has(hists, need_collapse))
Jiri Olsa367c53c2012-12-13 14:08:59 +0100453 root = &hists->entries_collapsed;
454 else
455 root = hists->entries_in;
456
457 next = rb_first(root);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200458 while (next != NULL) {
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100459 struct hist_entry *he, *pair;
Namhyung Kim56495a82015-01-08 09:45:47 +0900460 struct data__file *d;
461 int i;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200462
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100463 he = rb_entry(next, struct hist_entry, rb_node_in);
Jiri Olsa367c53c2012-12-13 14:08:59 +0100464 next = rb_next(&he->rb_node_in);
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100465
Namhyung Kim56495a82015-01-08 09:45:47 +0900466 data__for_each_file_new(i, d) {
467 pair = get_pair_data(he, d);
468 if (!pair)
469 continue;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200470
Namhyung Kim56495a82015-01-08 09:45:47 +0900471 switch (compute) {
472 case COMPUTE_DELTA:
Namhyung Kima1668c22017-02-10 16:36:11 +0900473 case COMPUTE_DELTA_ABS:
Namhyung Kim56495a82015-01-08 09:45:47 +0900474 compute_delta(he, pair);
475 break;
476 case COMPUTE_RATIO:
477 compute_ratio(he, pair);
478 break;
479 case COMPUTE_WEIGHTED_DIFF:
480 compute_wdiff(he, pair);
481 break;
482 default:
483 BUG_ON(1);
484 }
Jiri Olsa96c47f12012-10-05 16:44:42 +0200485 }
486 }
487}
488
489static int64_t cmp_doubles(double l, double r)
490{
491 if (l > r)
492 return -1;
493 else if (l < r)
494 return 1;
495 else
496 return 0;
497}
498
499static int64_t
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100500__hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
Jiri Olsa96c47f12012-10-05 16:44:42 +0200501 int c)
502{
503 switch (c) {
504 case COMPUTE_DELTA:
505 {
506 double l = left->diff.period_ratio_delta;
507 double r = right->diff.period_ratio_delta;
508
509 return cmp_doubles(l, r);
510 }
Namhyung Kima1668c22017-02-10 16:36:11 +0900511 case COMPUTE_DELTA_ABS:
512 {
513 double l = fabs(left->diff.period_ratio_delta);
514 double r = fabs(right->diff.period_ratio_delta);
515
516 return cmp_doubles(l, r);
517 }
Jiri Olsa96c47f12012-10-05 16:44:42 +0200518 case COMPUTE_RATIO:
519 {
520 double l = left->diff.period_ratio;
521 double r = right->diff.period_ratio;
522
523 return cmp_doubles(l, r);
524 }
Jiri Olsa81d5f952012-10-05 16:44:43 +0200525 case COMPUTE_WEIGHTED_DIFF:
526 {
527 s64 l = left->diff.wdiff;
528 s64 r = right->diff.wdiff;
529
530 return r - l;
531 }
Jiri Olsa96c47f12012-10-05 16:44:42 +0200532 default:
533 BUG_ON(1);
534 }
535
536 return 0;
537}
538
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100539static int64_t
540hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
Namhyung Kim56495a82015-01-08 09:45:47 +0900541 int c, int sort_idx)
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100542{
543 bool pairs_left = hist_entry__has_pairs(left);
544 bool pairs_right = hist_entry__has_pairs(right);
545 struct hist_entry *p_right, *p_left;
546
547 if (!pairs_left && !pairs_right)
548 return 0;
549
550 if (!pairs_left || !pairs_right)
551 return pairs_left ? -1 : 1;
552
Namhyung Kim56495a82015-01-08 09:45:47 +0900553 p_left = get_pair_data(left, &data__files[sort_idx]);
554 p_right = get_pair_data(right, &data__files[sort_idx]);
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100555
556 if (!p_left && !p_right)
557 return 0;
558
559 if (!p_left || !p_right)
560 return p_left ? -1 : 1;
561
562 /*
563 * We have 2 entries of same kind, let's
564 * make the data comparison.
565 */
566 return __hist_entry__cmp_compute(p_left, p_right, c);
567}
568
Namhyung Kime7024fc2014-12-27 14:06:29 +0900569static int64_t
Namhyung Kim566b5cf2015-01-08 09:45:48 +0900570hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right,
571 int c, int sort_idx)
572{
573 struct hist_entry *p_right, *p_left;
574
575 p_left = get_pair_data(left, &data__files[sort_idx]);
576 p_right = get_pair_data(right, &data__files[sort_idx]);
577
578 if (!p_left && !p_right)
579 return 0;
580
581 if (!p_left || !p_right)
582 return p_left ? -1 : 1;
583
Namhyung Kima1668c22017-02-10 16:36:11 +0900584 if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) {
Namhyung Kim566b5cf2015-01-08 09:45:48 +0900585 /*
586 * The delta can be computed without the baseline, but
587 * others are not. Put those entries which have no
588 * values below.
589 */
590 if (left->dummy && right->dummy)
591 return 0;
592
593 if (left->dummy || right->dummy)
594 return left->dummy ? 1 : -1;
595 }
596
597 return __hist_entry__cmp_compute(p_left, p_right, c);
598}
599
600static int64_t
Namhyung Kim87bbdf72015-01-08 09:45:46 +0900601hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused,
602 struct hist_entry *left __maybe_unused,
Namhyung Kime7024fc2014-12-27 14:06:29 +0900603 struct hist_entry *right __maybe_unused)
604{
605 return 0;
606}
607
608static int64_t
Namhyung Kim87bbdf72015-01-08 09:45:46 +0900609hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused,
610 struct hist_entry *left, struct hist_entry *right)
Namhyung Kime7024fc2014-12-27 14:06:29 +0900611{
Namhyung Kime7024fc2014-12-27 14:06:29 +0900612 if (left->stat.period == right->stat.period)
613 return 0;
614 return left->stat.period > right->stat.period ? 1 : -1;
615}
616
617static int64_t
Namhyung Kim56495a82015-01-08 09:45:47 +0900618hist_entry__cmp_delta(struct perf_hpp_fmt *fmt,
Namhyung Kim87bbdf72015-01-08 09:45:46 +0900619 struct hist_entry *left, struct hist_entry *right)
Namhyung Kime7024fc2014-12-27 14:06:29 +0900620{
Namhyung Kim56495a82015-01-08 09:45:47 +0900621 struct data__file *d = fmt_to_data_file(fmt);
622
623 return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx);
Namhyung Kime7024fc2014-12-27 14:06:29 +0900624}
625
626static int64_t
Namhyung Kima1668c22017-02-10 16:36:11 +0900627hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt,
628 struct hist_entry *left, struct hist_entry *right)
629{
630 struct data__file *d = fmt_to_data_file(fmt);
631
632 return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx);
633}
634
635static int64_t
Namhyung Kim56495a82015-01-08 09:45:47 +0900636hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt,
Namhyung Kim87bbdf72015-01-08 09:45:46 +0900637 struct hist_entry *left, struct hist_entry *right)
Namhyung Kime7024fc2014-12-27 14:06:29 +0900638{
Namhyung Kim56495a82015-01-08 09:45:47 +0900639 struct data__file *d = fmt_to_data_file(fmt);
640
641 return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx);
Namhyung Kime7024fc2014-12-27 14:06:29 +0900642}
643
644static int64_t
Namhyung Kim56495a82015-01-08 09:45:47 +0900645hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt,
Namhyung Kim87bbdf72015-01-08 09:45:46 +0900646 struct hist_entry *left, struct hist_entry *right)
Namhyung Kime7024fc2014-12-27 14:06:29 +0900647{
Namhyung Kim56495a82015-01-08 09:45:47 +0900648 struct data__file *d = fmt_to_data_file(fmt);
649
650 return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx);
Namhyung Kime7024fc2014-12-27 14:06:29 +0900651}
652
Namhyung Kim566b5cf2015-01-08 09:45:48 +0900653static int64_t
654hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused,
655 struct hist_entry *left, struct hist_entry *right)
656{
657 return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA,
658 sort_compute);
659}
660
661static int64_t
Namhyung Kima1668c22017-02-10 16:36:11 +0900662hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused,
663 struct hist_entry *left, struct hist_entry *right)
664{
665 return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS,
666 sort_compute);
667}
668
669static int64_t
Namhyung Kim566b5cf2015-01-08 09:45:48 +0900670hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused,
671 struct hist_entry *left, struct hist_entry *right)
672{
673 return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO,
674 sort_compute);
675}
676
677static int64_t
678hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused,
679 struct hist_entry *left, struct hist_entry *right)
680{
681 return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF,
682 sort_compute);
683}
684
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100685static void hists__process(struct hists *hists)
Jiri Olsaa06d1432012-10-05 16:44:40 +0200686{
Jiri Olsaa06d1432012-10-05 16:44:40 +0200687 if (show_baseline_only)
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100688 hists__baseline_only(hists);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200689
Namhyung Kim56495a82015-01-08 09:45:47 +0900690 hists__precompute(hists);
Namhyung Kim38259a12014-12-27 14:06:30 +0900691 hists__output_resort(hists, NULL);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200692
Jiri Olsad05e3aa2016-06-14 20:19:18 +0200693 hists__fprintf(hists, true, 0, 0, 0, stdout,
694 symbol_conf.use_callchain);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200695}
696
Jiri Olsa1d81c7f2012-12-01 21:56:03 +0100697static void data__fprintf(void)
698{
699 struct data__file *d;
700 int i;
701
702 fprintf(stdout, "# Data files:\n");
703
704 data__for_each_file(i, d)
705 fprintf(stdout, "# [%d] %s %s\n",
Jiri Olsaf5fc14122013-10-15 16:27:32 +0200706 d->idx, d->file.path,
Jiri Olsa1d81c7f2012-12-01 21:56:03 +0100707 !d->idx ? "(Baseline)" : "");
708
709 fprintf(stdout, "#\n");
710}
711
Jiri Olsaec308422013-03-25 00:02:01 +0100712static void data_process(void)
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200713{
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100714 struct perf_evlist *evlist_base = data__files[0].session->evlist;
715 struct perf_evsel *evsel_base;
Jiri Olsa863e4512012-09-06 17:46:55 +0200716 bool first = true;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200717
Arnaldo Carvalho de Meloe5cadb92016-06-23 11:26:15 -0300718 evlist__for_each_entry(evlist_base, evsel_base) {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300719 struct hists *hists_base = evsel__hists(evsel_base);
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100720 struct data__file *d;
721 int i;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200722
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100723 data__for_each_file_new(i, d) {
724 struct perf_evlist *evlist = d->session->evlist;
725 struct perf_evsel *evsel;
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300726 struct hists *hists;
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100727
728 evsel = evsel_match(evsel_base, evlist);
729 if (!evsel)
730 continue;
731
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300732 hists = evsel__hists(evsel);
733 d->hists = hists;
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100734
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300735 hists__match(hists_base, hists);
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100736
737 if (!show_baseline_only)
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300738 hists__link(hists_base, hists);
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100739 }
Jiri Olsa863e4512012-09-06 17:46:55 +0200740
741 fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100742 perf_evsel__name(evsel_base));
Jiri Olsa863e4512012-09-06 17:46:55 +0200743
744 first = false;
745
Jiri Olsa22aeb7f2012-12-01 22:00:00 +0100746 if (verbose || data__files_cnt > 2)
Jiri Olsa1d81c7f2012-12-01 21:56:03 +0100747 data__fprintf();
748
Kan Liangf9db0d02015-08-11 06:30:48 -0400749 /* Don't sort callchain for perf diff */
750 perf_evsel__reset_sample_bit(evsel_base, CALLCHAIN);
751
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -0300752 hists__process(hists_base);
Jiri Olsaec308422013-03-25 00:02:01 +0100753 }
754}
755
Jiri Olsac818b492012-12-01 21:57:04 +0100756static void data__free(struct data__file *d)
757{
758 int col;
759
760 for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
761 struct diff_hpp_fmt *fmt = &d->fmt[col];
762
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -0300763 zfree(&fmt->header);
Jiri Olsac818b492012-12-01 21:57:04 +0100764 }
765}
766
Jiri Olsaec308422013-03-25 00:02:01 +0100767static int __cmd_diff(void)
768{
769 struct data__file *d;
770 int ret = -EINVAL, i;
771
772 data__for_each_file(i, d) {
Jiri Olsaf5fc14122013-10-15 16:27:32 +0200773 d->session = perf_session__new(&d->file, false, &tool);
Jiri Olsaec308422013-03-25 00:02:01 +0100774 if (!d->session) {
Jiri Olsaf5fc14122013-10-15 16:27:32 +0200775 pr_err("Failed to open %s\n", d->file.path);
Taeung Song52e028342014-09-24 10:33:37 +0900776 ret = -1;
Jiri Olsaec308422013-03-25 00:02:01 +0100777 goto out_delete;
778 }
779
Arnaldo Carvalho de Melob7b61cb2015-03-03 11:58:45 -0300780 ret = perf_session__process_events(d->session);
Jiri Olsaec308422013-03-25 00:02:01 +0100781 if (ret) {
Jiri Olsaf5fc14122013-10-15 16:27:32 +0200782 pr_err("Failed to process %s\n", d->file.path);
Jiri Olsaec308422013-03-25 00:02:01 +0100783 goto out_delete;
784 }
785
786 perf_evlist__collapse_resort(d->session->evlist);
Jiri Olsa863e4512012-09-06 17:46:55 +0200787 }
788
Jiri Olsaec308422013-03-25 00:02:01 +0100789 data_process();
790
791 out_delete:
792 data__for_each_file(i, d) {
Arnaldo Carvalho de Meloe1446552016-06-22 10:02:16 -0300793 perf_session__delete(d->session);
Jiri Olsac818b492012-12-01 21:57:04 +0100794 data__free(d);
Jiri Olsaec308422013-03-25 00:02:01 +0100795 }
796
797 free(data__files);
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200798 return ret;
799}
800
Arnaldo Carvalho de Melo0422a4f2009-12-18 16:35:58 -0200801static const char * const diff_usage[] = {
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200802 "perf diff [<options>] [old_file] [new_file]",
Arnaldo Carvalho de Melo0422a4f2009-12-18 16:35:58 -0200803 NULL,
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200804};
805
806static const struct option options[] = {
Ian Munsiec0555642010-04-13 18:37:33 +1000807 OPT_INCR('v', "verbose", &verbose,
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200808 "be more verbose (show symbol address, etc)"),
Jiri Olsaa06d1432012-10-05 16:44:40 +0200809 OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
810 "Show only items with match in baseline"),
Jiri Olsa81d5f952012-10-05 16:44:43 +0200811 OPT_CALLBACK('c', "compute", &compute,
Namhyung Kima1668c22017-02-10 16:36:11 +0900812 "delta,delta-abs,ratio,wdiff:w1,w2 (default delta)",
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200813 "Entries differential computation selection",
814 setup_compute),
Jiri Olsa61949b22012-10-05 16:44:44 +0200815 OPT_BOOLEAN('p', "period", &show_period,
816 "Show period values."),
Jiri Olsaed279da2012-10-05 16:44:45 +0200817 OPT_BOOLEAN('F', "formula", &show_formula,
818 "Show formula."),
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200819 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
820 "dump raw trace in ASCII"),
821 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
David Ahern6b1f3422015-03-24 09:51:57 -0600822 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
823 "file", "kallsyms pathname"),
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200824 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
825 "load module symbols - WARNING: use only with -k and LIVE kernel"),
Arnaldo Carvalho de Meloc410a332009-12-15 20:04:41 -0200826 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
827 "only consider symbols in these dsos"),
828 OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
829 "only consider symbols in these comms"),
830 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
831 "only consider these symbols"),
Arnaldo Carvalho de Meloc351c282009-12-16 13:49:27 -0200832 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
Namhyung Kima2ce0672014-03-04 09:06:42 +0900833 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
834 " Please refer the man page for the complete list."),
Wang Nan8b8ca6e2015-03-20 02:57:52 +0000835 OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
Arnaldo Carvalho de Meloc351c282009-12-16 13:49:27 -0200836 "separator for columns, no spaces will be added between "
837 "columns '.' is reserved."),
He Kuanga7066702016-05-19 11:47:37 +0000838 OPT_CALLBACK(0, "symfs", NULL, "directory",
839 "Look for files with symbols relative to this directory",
840 symbol__config_symfs),
Jiri Olsa5f3f8d32012-11-25 23:10:20 +0100841 OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
Namhyung Kim8810f6c2014-02-07 12:06:07 +0900842 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
843 "How to display percentage of filtered entries", parse_filter_percentage),
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200844 OPT_END()
845};
846
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100847static double baseline_percent(struct hist_entry *he)
Jiri Olsa1d778222012-10-04 21:49:39 +0900848{
Namhyung Kim8810f6c2014-02-07 12:06:07 +0900849 u64 total = hists__total_period(he->hists);
850
851 return 100.0 * he->stat.period / total;
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100852}
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200853
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100854static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
855 struct perf_hpp *hpp, struct hist_entry *he)
856{
857 struct diff_hpp_fmt *dfmt =
858 container_of(fmt, struct diff_hpp_fmt, fmt);
859 double percent = baseline_percent(he);
860 char pfmt[20] = " ";
861
862 if (!he->dummy) {
863 scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1);
864 return percent_color_snprintf(hpp->buf, hpp->size,
865 pfmt, percent);
866 } else
867 return scnprintf(hpp->buf, hpp->size, "%*s",
868 dfmt->header_width, pfmt);
869}
870
871static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
872{
873 double percent = baseline_percent(he);
874 const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
875 int ret = 0;
876
877 if (!he->dummy)
878 ret = scnprintf(buf, size, fmt, percent);
879
880 return ret;
881}
882
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +0530883static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
884 struct perf_hpp *hpp, struct hist_entry *he,
885 int comparison_method)
886{
887 struct diff_hpp_fmt *dfmt =
888 container_of(fmt, struct diff_hpp_fmt, fmt);
889 struct hist_entry *pair = get_pair_fmt(he, dfmt);
890 double diff;
Ramkumar Ramachandraa5846e22013-12-30 13:32:35 +0530891 s64 wdiff;
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +0530892 char pfmt[20] = " ";
893
894 if (!pair)
Namhyung Kimec3d07c2014-12-27 14:06:31 +0900895 goto no_print;
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +0530896
897 switch (comparison_method) {
898 case COMPUTE_DELTA:
899 if (pair->diff.computed)
900 diff = pair->diff.period_ratio_delta;
901 else
902 diff = compute_delta(he, pair);
903
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +0530904 scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1);
905 return percent_color_snprintf(hpp->buf, hpp->size,
906 pfmt, diff);
Ramkumar Ramachandra1f513b22013-12-30 13:04:20 +0530907 case COMPUTE_RATIO:
908 if (he->dummy)
909 goto dummy_print;
910 if (pair->diff.computed)
911 diff = pair->diff.period_ratio;
912 else
913 diff = compute_ratio(he, pair);
914
915 scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width);
916 return value_color_snprintf(hpp->buf, hpp->size,
917 pfmt, diff);
Ramkumar Ramachandraa5846e22013-12-30 13:32:35 +0530918 case COMPUTE_WEIGHTED_DIFF:
919 if (he->dummy)
920 goto dummy_print;
921 if (pair->diff.computed)
922 wdiff = pair->diff.wdiff;
923 else
924 wdiff = compute_wdiff(he, pair);
925
926 scnprintf(pfmt, 20, "%%14ld", dfmt->header_width);
927 return color_snprintf(hpp->buf, hpp->size,
928 get_percent_color(wdiff),
929 pfmt, wdiff);
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +0530930 default:
931 BUG_ON(1);
932 }
933dummy_print:
934 return scnprintf(hpp->buf, hpp->size, "%*s",
Namhyung Kimec3d07c2014-12-27 14:06:31 +0900935 dfmt->header_width, "N/A");
936no_print:
937 return scnprintf(hpp->buf, hpp->size, "%*s",
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +0530938 dfmt->header_width, pfmt);
939}
940
941static int hpp__color_delta(struct perf_hpp_fmt *fmt,
942 struct perf_hpp *hpp, struct hist_entry *he)
943{
944 return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA);
945}
946
Ramkumar Ramachandra1f513b22013-12-30 13:04:20 +0530947static int hpp__color_ratio(struct perf_hpp_fmt *fmt,
948 struct perf_hpp *hpp, struct hist_entry *he)
949{
950 return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO);
951}
952
Ramkumar Ramachandraa5846e22013-12-30 13:32:35 +0530953static int hpp__color_wdiff(struct perf_hpp_fmt *fmt,
954 struct perf_hpp *hpp, struct hist_entry *he)
955{
956 return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
957}
958
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100959static void
960hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
961{
962 switch (idx) {
963 case PERF_HPP_DIFF__PERIOD_BASELINE:
964 scnprintf(buf, size, "%" PRIu64, he->stat.period);
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200965 break;
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100966
967 default:
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200968 break;
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100969 }
970}
971
972static void
973hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
974 int idx, char *buf, size_t size)
975{
976 double diff;
977 double ratio;
978 s64 wdiff;
979
980 switch (idx) {
981 case PERF_HPP_DIFF__DELTA:
Namhyung Kima1668c22017-02-10 16:36:11 +0900982 case PERF_HPP_DIFF__DELTA_ABS:
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100983 if (pair->diff.computed)
984 diff = pair->diff.period_ratio_delta;
985 else
Jiri Olsaef358e62012-10-21 23:31:51 +0200986 diff = compute_delta(he, pair);
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100987
Namhyung Kimec3d07c2014-12-27 14:06:31 +0900988 scnprintf(buf, size, "%+4.2F%%", diff);
Jiri Olsa81d5f952012-10-05 16:44:43 +0200989 break;
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100990
991 case PERF_HPP_DIFF__RATIO:
992 /* No point for ratio number if we are dummy.. */
Namhyung Kimec3d07c2014-12-27 14:06:31 +0900993 if (he->dummy) {
994 scnprintf(buf, size, "N/A");
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100995 break;
Namhyung Kimec3d07c2014-12-27 14:06:31 +0900996 }
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100997
998 if (pair->diff.computed)
999 ratio = pair->diff.period_ratio;
1000 else
Jiri Olsaef358e62012-10-21 23:31:51 +02001001 ratio = compute_ratio(he, pair);
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001002
1003 if (ratio > 0.0)
1004 scnprintf(buf, size, "%14.6F", ratio);
1005 break;
1006
1007 case PERF_HPP_DIFF__WEIGHTED_DIFF:
1008 /* No point for wdiff number if we are dummy.. */
Namhyung Kimec3d07c2014-12-27 14:06:31 +09001009 if (he->dummy) {
1010 scnprintf(buf, size, "N/A");
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001011 break;
Namhyung Kimec3d07c2014-12-27 14:06:31 +09001012 }
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001013
1014 if (pair->diff.computed)
1015 wdiff = pair->diff.wdiff;
1016 else
Jiri Olsaef358e62012-10-21 23:31:51 +02001017 wdiff = compute_wdiff(he, pair);
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001018
1019 if (wdiff != 0)
1020 scnprintf(buf, size, "%14ld", wdiff);
1021 break;
1022
1023 case PERF_HPP_DIFF__FORMULA:
Jiri Olsaef358e62012-10-21 23:31:51 +02001024 formula_fprintf(he, pair, buf, size);
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001025 break;
1026
1027 case PERF_HPP_DIFF__PERIOD:
1028 scnprintf(buf, size, "%" PRIu64, pair->stat.period);
1029 break;
1030
Jiri Olsa7aaf6b32012-10-05 16:44:41 +02001031 default:
1032 BUG_ON(1);
1033 };
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001034}
1035
Jiri Olsa22aeb7f2012-12-01 22:00:00 +01001036static void
1037__hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt,
1038 char *buf, size_t size)
1039{
Jiri Olsa5f3f8d32012-11-25 23:10:20 +01001040 struct hist_entry *pair = get_pair_fmt(he, dfmt);
Jiri Olsa22aeb7f2012-12-01 22:00:00 +01001041 int idx = dfmt->idx;
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001042
1043 /* baseline is special */
1044 if (idx == PERF_HPP_DIFF__BASELINE)
1045 hpp__entry_baseline(he, buf, size);
1046 else {
1047 if (pair)
1048 hpp__entry_pair(he, pair, idx, buf, size);
1049 else
1050 hpp__entry_unpair(he, idx, buf, size);
1051 }
1052}
1053
1054static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,
1055 struct hist_entry *he)
1056{
1057 struct diff_hpp_fmt *dfmt =
1058 container_of(_fmt, struct diff_hpp_fmt, fmt);
1059 char buf[MAX_COL_WIDTH] = " ";
1060
Jiri Olsa22aeb7f2012-12-01 22:00:00 +01001061 __hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH);
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001062
1063 if (symbol_conf.field_sep)
1064 return scnprintf(hpp->buf, hpp->size, "%s", buf);
1065 else
1066 return scnprintf(hpp->buf, hpp->size, "%*s",
1067 dfmt->header_width, buf);
1068}
1069
Namhyung Kim94a07932014-03-10 16:43:52 +09001070static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
Jiri Olsa74bb43f2016-08-07 17:28:27 +02001071 struct hists *hists __maybe_unused,
Jiri Olsa29659ab2016-08-07 17:28:30 +02001072 int line __maybe_unused,
1073 int *span __maybe_unused)
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001074{
1075 struct diff_hpp_fmt *dfmt =
1076 container_of(fmt, struct diff_hpp_fmt, fmt);
1077
1078 BUG_ON(!dfmt->header);
1079 return scnprintf(hpp->buf, hpp->size, dfmt->header);
1080}
1081
1082static int hpp__width(struct perf_hpp_fmt *fmt,
Namhyung Kim94a07932014-03-10 16:43:52 +09001083 struct perf_hpp *hpp __maybe_unused,
Jiri Olsada1b0402016-06-14 20:19:20 +02001084 struct hists *hists __maybe_unused)
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001085{
1086 struct diff_hpp_fmt *dfmt =
1087 container_of(fmt, struct diff_hpp_fmt, fmt);
1088
1089 BUG_ON(dfmt->header_width <= 0);
1090 return dfmt->header_width;
1091}
1092
Jiri Olsa22aeb7f2012-12-01 22:00:00 +01001093static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt)
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001094{
1095#define MAX_HEADER_NAME 100
1096 char buf_indent[MAX_HEADER_NAME];
1097 char buf[MAX_HEADER_NAME];
1098 const char *header = NULL;
1099 int width = 0;
1100
1101 BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX);
1102 header = columns[dfmt->idx].name;
1103 width = columns[dfmt->idx].width;
1104
1105 /* Only our defined HPP fmts should appear here. */
1106 BUG_ON(!header);
1107
Jiri Olsa22aeb7f2012-12-01 22:00:00 +01001108 if (data__files_cnt > 2)
1109 scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx);
1110
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001111#define NAME (data__files_cnt > 2 ? buf : header)
1112 dfmt->header_width = width;
1113 width = (int) strlen(NAME);
1114 if (dfmt->header_width < width)
1115 dfmt->header_width = width;
1116
1117 scnprintf(buf_indent, MAX_HEADER_NAME, "%*s",
1118 dfmt->header_width, NAME);
1119
1120 dfmt->header = strdup(buf_indent);
1121#undef MAX_HEADER_NAME
1122#undef NAME
1123}
1124
Jiri Olsac818b492012-12-01 21:57:04 +01001125static void data__hpp_register(struct data__file *d, int idx)
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001126{
Jiri Olsac818b492012-12-01 21:57:04 +01001127 struct diff_hpp_fmt *dfmt = &d->fmt[idx];
1128 struct perf_hpp_fmt *fmt = &dfmt->fmt;
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001129
Jiri Olsac818b492012-12-01 21:57:04 +01001130 dfmt->idx = idx;
1131
1132 fmt->header = hpp__header;
1133 fmt->width = hpp__width;
1134 fmt->entry = hpp__entry_global;
Namhyung Kime7024fc2014-12-27 14:06:29 +09001135 fmt->cmp = hist_entry__cmp_nop;
1136 fmt->collapse = hist_entry__cmp_nop;
Jiri Olsac818b492012-12-01 21:57:04 +01001137
1138 /* TODO more colors */
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +05301139 switch (idx) {
1140 case PERF_HPP_DIFF__BASELINE:
Jiri Olsac818b492012-12-01 21:57:04 +01001141 fmt->color = hpp__color_baseline;
Namhyung Kime7024fc2014-12-27 14:06:29 +09001142 fmt->sort = hist_entry__cmp_baseline;
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +05301143 break;
1144 case PERF_HPP_DIFF__DELTA:
1145 fmt->color = hpp__color_delta;
Namhyung Kime7024fc2014-12-27 14:06:29 +09001146 fmt->sort = hist_entry__cmp_delta;
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +05301147 break;
Ramkumar Ramachandra1f513b22013-12-30 13:04:20 +05301148 case PERF_HPP_DIFF__RATIO:
1149 fmt->color = hpp__color_ratio;
Namhyung Kime7024fc2014-12-27 14:06:29 +09001150 fmt->sort = hist_entry__cmp_ratio;
Ramkumar Ramachandra1f513b22013-12-30 13:04:20 +05301151 break;
Ramkumar Ramachandraa5846e22013-12-30 13:32:35 +05301152 case PERF_HPP_DIFF__WEIGHTED_DIFF:
1153 fmt->color = hpp__color_wdiff;
Namhyung Kime7024fc2014-12-27 14:06:29 +09001154 fmt->sort = hist_entry__cmp_wdiff;
Ramkumar Ramachandraa5846e22013-12-30 13:32:35 +05301155 break;
Namhyung Kima1668c22017-02-10 16:36:11 +09001156 case PERF_HPP_DIFF__DELTA_ABS:
1157 fmt->color = hpp__color_delta;
1158 fmt->sort = hist_entry__cmp_delta_abs;
1159 break;
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +05301160 default:
Namhyung Kime7024fc2014-12-27 14:06:29 +09001161 fmt->sort = hist_entry__cmp_nop;
Ramkumar Ramachandra01f10bc2013-12-30 13:04:19 +05301162 break;
1163 }
Jiri Olsac818b492012-12-01 21:57:04 +01001164
Jiri Olsa22aeb7f2012-12-01 22:00:00 +01001165 init_header(d, dfmt);
Jiri Olsac818b492012-12-01 21:57:04 +01001166 perf_hpp__column_register(fmt);
Namhyung Kime7024fc2014-12-27 14:06:29 +09001167 perf_hpp__register_sort_field(fmt);
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001168}
1169
Namhyung Kim566b5cf2015-01-08 09:45:48 +09001170static int ui_init(void)
Jiri Olsa345dc0b2013-02-03 20:08:34 +01001171{
Jiri Olsac818b492012-12-01 21:57:04 +01001172 struct data__file *d;
Namhyung Kim566b5cf2015-01-08 09:45:48 +09001173 struct perf_hpp_fmt *fmt;
Jiri Olsac818b492012-12-01 21:57:04 +01001174 int i;
Jiri Olsa1d778222012-10-04 21:49:39 +09001175
Jiri Olsac818b492012-12-01 21:57:04 +01001176 data__for_each_file(i, d) {
Jiri Olsaed279da2012-10-05 16:44:45 +02001177
Jiri Olsac818b492012-12-01 21:57:04 +01001178 /*
1179 * Baseline or compute realted columns:
1180 *
1181 * PERF_HPP_DIFF__BASELINE
1182 * PERF_HPP_DIFF__DELTA
1183 * PERF_HPP_DIFF__RATIO
1184 * PERF_HPP_DIFF__WEIGHTED_DIFF
1185 */
1186 data__hpp_register(d, i ? compute_2_hpp[compute] :
1187 PERF_HPP_DIFF__BASELINE);
1188
1189 /*
1190 * And the rest:
1191 *
1192 * PERF_HPP_DIFF__FORMULA
1193 * PERF_HPP_DIFF__PERIOD
1194 * PERF_HPP_DIFF__PERIOD_BASELINE
1195 */
1196 if (show_formula && i)
1197 data__hpp_register(d, PERF_HPP_DIFF__FORMULA);
1198
1199 if (show_period)
1200 data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
1201 PERF_HPP_DIFF__PERIOD_BASELINE);
Jiri Olsa61949b22012-10-05 16:44:44 +02001202 }
Namhyung Kim566b5cf2015-01-08 09:45:48 +09001203
1204 if (!sort_compute)
1205 return 0;
1206
1207 /*
1208 * Prepend an fmt to sort on columns at 'sort_compute' first.
1209 * This fmt is added only to the sort list but not to the
1210 * output fields list.
1211 *
1212 * Note that this column (data) can be compared twice - one
1213 * for this 'sort_compute' fmt and another for the normal
1214 * diff_hpp_fmt. But it shouldn't a problem as most entries
1215 * will be sorted out by first try or baseline and comparing
1216 * is not a costly operation.
1217 */
1218 fmt = zalloc(sizeof(*fmt));
1219 if (fmt == NULL) {
1220 pr_err("Memory allocation failed\n");
1221 return -1;
1222 }
1223
1224 fmt->cmp = hist_entry__cmp_nop;
1225 fmt->collapse = hist_entry__cmp_nop;
1226
1227 switch (compute) {
1228 case COMPUTE_DELTA:
1229 fmt->sort = hist_entry__cmp_delta_idx;
1230 break;
1231 case COMPUTE_RATIO:
1232 fmt->sort = hist_entry__cmp_ratio_idx;
1233 break;
1234 case COMPUTE_WEIGHTED_DIFF:
1235 fmt->sort = hist_entry__cmp_wdiff_idx;
1236 break;
Namhyung Kima1668c22017-02-10 16:36:11 +09001237 case COMPUTE_DELTA_ABS:
1238 fmt->sort = hist_entry__cmp_delta_abs_idx;
1239 break;
Namhyung Kim566b5cf2015-01-08 09:45:48 +09001240 default:
1241 BUG_ON(1);
1242 }
1243
Namhyung Kima1c9f972017-01-18 14:14:57 +09001244 perf_hpp__prepend_sort_field(fmt);
Namhyung Kim566b5cf2015-01-08 09:45:48 +09001245 return 0;
Jiri Olsa1d778222012-10-04 21:49:39 +09001246}
1247
Jiri Olsaec308422013-03-25 00:02:01 +01001248static int data_init(int argc, const char **argv)
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -02001249{
Jiri Olsaec308422013-03-25 00:02:01 +01001250 struct data__file *d;
1251 static const char *defaults[] = {
1252 "perf.data.old",
1253 "perf.data",
1254 };
Jiri Olsa22aeb7f2012-12-01 22:00:00 +01001255 bool use_default = true;
Jiri Olsaec308422013-03-25 00:02:01 +01001256 int i;
1257
1258 data__files_cnt = 2;
1259
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -02001260 if (argc) {
Jiri Olsa22aeb7f2012-12-01 22:00:00 +01001261 if (argc == 1)
Jiri Olsaec308422013-03-25 00:02:01 +01001262 defaults[1] = argv[0];
Jiri Olsa22aeb7f2012-12-01 22:00:00 +01001263 else {
1264 data__files_cnt = argc;
1265 use_default = false;
1266 }
Dongsheng Yangd8d96082013-12-06 17:25:52 -05001267 } else if (perf_guest) {
Jiri Olsaec308422013-03-25 00:02:01 +01001268 defaults[0] = "perf.data.host";
1269 defaults[1] = "perf.data.guest";
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -02001270 }
1271
Jiri Olsa5f3f8d32012-11-25 23:10:20 +01001272 if (sort_compute >= (unsigned int) data__files_cnt) {
1273 pr_err("Order option out of limit.\n");
1274 return -EINVAL;
1275 }
1276
Jiri Olsaec308422013-03-25 00:02:01 +01001277 data__files = zalloc(sizeof(*data__files) * data__files_cnt);
1278 if (!data__files)
1279 return -ENOMEM;
1280
1281 data__for_each_file(i, d) {
Jiri Olsaf5fc14122013-10-15 16:27:32 +02001282 struct perf_data_file *file = &d->file;
1283
1284 file->path = use_default ? defaults[i] : argv[i];
1285 file->mode = PERF_DATA_MODE_READ,
1286 file->force = force,
1287
Jiri Olsaec308422013-03-25 00:02:01 +01001288 d->idx = i;
1289 }
1290
1291 return 0;
1292}
1293
1294int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
1295{
Kan Liang9ab1f502014-10-22 15:02:41 -04001296 int ret = hists__init();
1297
1298 if (ret < 0)
1299 return ret;
1300
Jiri Olsaec308422013-03-25 00:02:01 +01001301 argc = parse_options(argc, argv, options, diff_usage, 0);
1302
Namhyung Kim0a7e6d12014-08-12 15:40:45 +09001303 if (symbol__init(NULL) < 0)
Arnaldo Carvalho de Melo655000e2009-12-15 20:04:40 -02001304 return -1;
1305
Jiri Olsaec308422013-03-25 00:02:01 +01001306 if (data_init(argc, argv) < 0)
1307 return -1;
1308
Namhyung Kim566b5cf2015-01-08 09:45:48 +09001309 if (ui_init() < 0)
1310 return -1;
Jiri Olsa1d778222012-10-04 21:49:39 +09001311
Namhyung Kim512ae1b2014-03-18 11:31:39 +09001312 sort__mode = SORT_MODE__DIFF;
1313
Namhyung Kim40184c42015-12-23 02:07:01 +09001314 if (setup_sorting(NULL) < 0)
Namhyung Kim55309982013-02-06 14:57:16 +09001315 usage_with_options(diff_usage, options);
1316
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -02001317 setup_pager();
Arnaldo Carvalho de Meloc351c282009-12-16 13:49:27 -02001318
Namhyung Kim08e71542013-04-03 21:26:19 +09001319 sort__setup_elide(NULL);
Arnaldo Carvalho de Meloc351c282009-12-16 13:49:27 -02001320
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -02001321 return __cmd_diff();
1322}