blob: 7787ee24a18afb1328dd339b08e5698c3af94e6f [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"
19
20#include <stdlib.h>
Jiri Olsa345dc0b2013-02-03 20:08:34 +010021#include <math.h>
22
23/* Diff command specific HPP columns. */
24enum {
25 PERF_HPP_DIFF__BASELINE,
26 PERF_HPP_DIFF__PERIOD,
27 PERF_HPP_DIFF__PERIOD_BASELINE,
28 PERF_HPP_DIFF__DELTA,
29 PERF_HPP_DIFF__RATIO,
30 PERF_HPP_DIFF__WEIGHTED_DIFF,
31 PERF_HPP_DIFF__FORMULA,
32
33 PERF_HPP_DIFF__MAX_INDEX
34};
35
36struct diff_hpp_fmt {
37 struct perf_hpp_fmt fmt;
38 int idx;
39 char *header;
40 int header_width;
41};
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -020042
Jiri Olsaec308422013-03-25 00:02:01 +010043struct data__file {
44 struct perf_session *session;
45 const char *file;
46 int idx;
Jiri Olsac818b492012-12-01 21:57:04 +010047 struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX];
Jiri Olsaec308422013-03-25 00:02:01 +010048};
49
50static struct data__file *data__files;
51static int data__files_cnt;
52
53#define data__for_each_file_start(i, d, s) \
54 for (i = s, d = &data__files[s]; \
55 i < data__files_cnt; \
56 i++, d = &data__files[i])
57
58#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
59
60static char diff__default_sort_order[] = "dso,symbol";
61static bool force;
Jiri Olsa61949b22012-10-05 16:44:44 +020062static bool show_period;
Jiri Olsaed279da2012-10-05 16:44:45 +020063static bool show_formula;
Jiri Olsaa06d1432012-10-05 16:44:40 +020064static bool show_baseline_only;
Jiri Olsa96c47f12012-10-05 16:44:42 +020065static bool sort_compute;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -020066
Jiri Olsa81d5f952012-10-05 16:44:43 +020067static s64 compute_wdiff_w1;
68static s64 compute_wdiff_w2;
69
Jiri Olsa7aaf6b32012-10-05 16:44:41 +020070enum {
71 COMPUTE_DELTA,
72 COMPUTE_RATIO,
Jiri Olsa81d5f952012-10-05 16:44:43 +020073 COMPUTE_WEIGHTED_DIFF,
Jiri Olsa7aaf6b32012-10-05 16:44:41 +020074 COMPUTE_MAX,
75};
76
77const char *compute_names[COMPUTE_MAX] = {
78 [COMPUTE_DELTA] = "delta",
79 [COMPUTE_RATIO] = "ratio",
Jiri Olsa81d5f952012-10-05 16:44:43 +020080 [COMPUTE_WEIGHTED_DIFF] = "wdiff",
Jiri Olsa7aaf6b32012-10-05 16:44:41 +020081};
82
83static int compute;
84
Jiri Olsa345dc0b2013-02-03 20:08:34 +010085static int compute_2_hpp[COMPUTE_MAX] = {
86 [COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA,
87 [COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
88 [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
89};
90
91#define MAX_COL_WIDTH 70
92
93static struct header_column {
94 const char *name;
95 int width;
96} columns[PERF_HPP_DIFF__MAX_INDEX] = {
97 [PERF_HPP_DIFF__BASELINE] = {
98 .name = "Baseline",
99 },
100 [PERF_HPP_DIFF__PERIOD] = {
101 .name = "Period",
102 .width = 14,
103 },
104 [PERF_HPP_DIFF__PERIOD_BASELINE] = {
105 .name = "Base period",
106 .width = 14,
107 },
108 [PERF_HPP_DIFF__DELTA] = {
109 .name = "Delta",
110 .width = 7,
111 },
112 [PERF_HPP_DIFF__RATIO] = {
113 .name = "Ratio",
114 .width = 14,
115 },
116 [PERF_HPP_DIFF__WEIGHTED_DIFF] = {
117 .name = "Weighted diff",
118 .width = 14,
119 },
120 [PERF_HPP_DIFF__FORMULA] = {
121 .name = "Formula",
122 .width = MAX_COL_WIDTH,
123 }
124};
125
Jiri Olsa81d5f952012-10-05 16:44:43 +0200126static int setup_compute_opt_wdiff(char *opt)
127{
128 char *w1_str = opt;
129 char *w2_str;
130
131 int ret = -EINVAL;
132
133 if (!opt)
134 goto out;
135
136 w2_str = strchr(opt, ',');
137 if (!w2_str)
138 goto out;
139
140 *w2_str++ = 0x0;
141 if (!*w2_str)
142 goto out;
143
144 compute_wdiff_w1 = strtol(w1_str, NULL, 10);
145 compute_wdiff_w2 = strtol(w2_str, NULL, 10);
146
147 if (!compute_wdiff_w1 || !compute_wdiff_w2)
148 goto out;
149
150 pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
151 compute_wdiff_w1, compute_wdiff_w2);
152
153 ret = 0;
154
155 out:
156 if (ret)
157 pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
158
159 return ret;
160}
161
162static int setup_compute_opt(char *opt)
163{
164 if (compute == COMPUTE_WEIGHTED_DIFF)
165 return setup_compute_opt_wdiff(opt);
166
167 if (opt) {
168 pr_err("Failed: extra option specified '%s'", opt);
169 return -EINVAL;
170 }
171
172 return 0;
173}
174
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200175static int setup_compute(const struct option *opt, const char *str,
176 int unset __maybe_unused)
177{
178 int *cp = (int *) opt->value;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200179 char *cstr = (char *) str;
180 char buf[50];
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200181 unsigned i;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200182 char *option;
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200183
184 if (!str) {
185 *cp = COMPUTE_DELTA;
186 return 0;
187 }
188
Jiri Olsa96c47f12012-10-05 16:44:42 +0200189 if (*str == '+') {
190 sort_compute = true;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200191 cstr = (char *) ++str;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200192 if (!*str)
193 return 0;
194 }
195
Jiri Olsa81d5f952012-10-05 16:44:43 +0200196 option = strchr(str, ':');
197 if (option) {
198 unsigned len = option++ - str;
199
200 /*
201 * The str data are not writeable, so we need
202 * to use another buffer.
203 */
204
205 /* No option value is longer. */
206 if (len >= sizeof(buf))
207 return -EINVAL;
208
209 strncpy(buf, str, len);
210 buf[len] = 0x0;
211 cstr = buf;
212 }
213
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200214 for (i = 0; i < COMPUTE_MAX; i++)
Jiri Olsa81d5f952012-10-05 16:44:43 +0200215 if (!strcmp(cstr, compute_names[i])) {
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200216 *cp = i;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200217 return setup_compute_opt(option);
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200218 }
219
220 pr_err("Failed: '%s' is not computation method "
Jiri Olsa81d5f952012-10-05 16:44:43 +0200221 "(use 'delta','ratio' or 'wdiff')\n", str);
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200222 return -EINVAL;
223}
224
Jiri Olsa05472da2012-11-28 14:52:40 +0100225double perf_diff__period_percent(struct hist_entry *he, u64 period)
Jiri Olsa96c47f12012-10-05 16:44:42 +0200226{
227 u64 total = he->hists->stats.total_period;
228 return (period * 100.0) / total;
229}
230
Jiri Olsa05472da2012-11-28 14:52:40 +0100231double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair)
Jiri Olsa96c47f12012-10-05 16:44:42 +0200232{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100233 double old_percent = perf_diff__period_percent(he, he->stat.period);
234 double new_percent = perf_diff__period_percent(pair, pair->stat.period);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200235
Jiri Olsa9af303e2012-12-01 21:15:40 +0100236 pair->diff.period_ratio_delta = new_percent - old_percent;
237 pair->diff.computed = true;
238 return pair->diff.period_ratio_delta;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200239}
240
Jiri Olsa05472da2012-11-28 14:52:40 +0100241double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair)
Jiri Olsa96c47f12012-10-05 16:44:42 +0200242{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100243 double old_period = he->stat.period ?: 1;
244 double new_period = pair->stat.period;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200245
Jiri Olsa9af303e2012-12-01 21:15:40 +0100246 pair->diff.computed = true;
247 pair->diff.period_ratio = new_period / old_period;
248 return pair->diff.period_ratio;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200249}
250
Jiri Olsa05472da2012-11-28 14:52:40 +0100251s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
Jiri Olsa81d5f952012-10-05 16:44:43 +0200252{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100253 u64 old_period = he->stat.period;
254 u64 new_period = pair->stat.period;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200255
Jiri Olsa9af303e2012-12-01 21:15:40 +0100256 pair->diff.computed = true;
257 pair->diff.wdiff = new_period * compute_wdiff_w2 -
258 old_period * compute_wdiff_w1;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200259
Jiri Olsa9af303e2012-12-01 21:15:40 +0100260 return pair->diff.wdiff;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200261}
262
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100263static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
264 char *buf, size_t size)
Jiri Olsaed279da2012-10-05 16:44:45 +0200265{
Jiri Olsaed279da2012-10-05 16:44:45 +0200266 return scnprintf(buf, size,
267 "(%" PRIu64 " * 100 / %" PRIu64 ") - "
268 "(%" PRIu64 " * 100 / %" PRIu64 ")",
Jiri Olsa9af303e2012-12-01 21:15:40 +0100269 pair->stat.period, pair->hists->stats.total_period,
270 he->stat.period, he->hists->stats.total_period);
Jiri Olsaed279da2012-10-05 16:44:45 +0200271}
272
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100273static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
274 char *buf, size_t size)
Jiri Olsaed279da2012-10-05 16:44:45 +0200275{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100276 double old_period = he->stat.period;
277 double new_period = pair->stat.period;
Jiri Olsaed279da2012-10-05 16:44:45 +0200278
279 return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
280}
281
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100282static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair,
283 char *buf, size_t size)
Jiri Olsaed279da2012-10-05 16:44:45 +0200284{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100285 u64 old_period = he->stat.period;
286 u64 new_period = pair->stat.period;
Jiri Olsaed279da2012-10-05 16:44:45 +0200287
288 return scnprintf(buf, size,
289 "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
290 new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
291}
292
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100293int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair,
294 char *buf, size_t size)
Jiri Olsaed279da2012-10-05 16:44:45 +0200295{
296 switch (compute) {
297 case COMPUTE_DELTA:
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100298 return formula_delta(he, pair, buf, size);
Jiri Olsaed279da2012-10-05 16:44:45 +0200299 case COMPUTE_RATIO:
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100300 return formula_ratio(he, pair, buf, size);
Jiri Olsaed279da2012-10-05 16:44:45 +0200301 case COMPUTE_WEIGHTED_DIFF:
Jiri Olsaf4c8bae2012-11-28 14:52:41 +0100302 return formula_wdiff(he, pair, buf, size);
Jiri Olsaed279da2012-10-05 16:44:45 +0200303 default:
304 BUG_ON(1);
305 }
306
307 return -1;
308}
309
Arnaldo Carvalho de Melo1c02c4d2010-05-10 13:04:11 -0300310static int hists__add_entry(struct hists *self,
Andi Kleen05484292013-01-24 16:10:29 +0100311 struct addr_location *al, u64 period,
312 u64 weight)
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200313{
Andi Kleen05484292013-01-24 16:10:29 +0100314 if (__hists__add_entry(self, al, NULL, period, weight) != NULL)
Arnaldo Carvalho de Melo28e2a102010-05-09 13:02:23 -0300315 return 0;
316 return -ENOMEM;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200317}
318
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300319static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
Arnaldo Carvalho de Melod20deb62011-11-25 08:19:45 -0200320 union perf_event *event,
Arnaldo Carvalho de Melo8d50e5b2011-01-29 13:02:00 -0200321 struct perf_sample *sample,
Jiri Olsa863e4512012-09-06 17:46:55 +0200322 struct perf_evsel *evsel,
Arnaldo Carvalho de Melo743eb862011-11-28 07:56:39 -0200323 struct machine *machine)
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200324{
325 struct addr_location al;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200326
Arnaldo Carvalho de Melo743eb862011-11-28 07:56:39 -0200327 if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200328 pr_warning("problem processing %d event, skipping it.\n",
329 event->header.type);
330 return -1;
331 }
332
Jiri Olsad88c48f2012-10-05 16:44:46 +0200333 if (al.filtered)
Arnaldo Carvalho de Meloc410a332009-12-15 20:04:41 -0200334 return 0;
335
Andi Kleen05484292013-01-24 16:10:29 +0100336 if (hists__add_entry(&evsel->hists, &al, sample->period, sample->weight)) {
Arnaldo Carvalho de Meloc82ee822010-05-14 14:19:35 -0300337 pr_warning("problem incrementing symbol period, skipping event\n");
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200338 return -1;
339 }
340
Jiri Olsa863e4512012-09-06 17:46:55 +0200341 evsel->hists.stats.total_period += sample->period;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200342 return 0;
343}
344
Jiri Olsa863e4512012-09-06 17:46:55 +0200345static struct perf_tool tool = {
346 .sample = diff__process_sample_event,
347 .mmap = perf_event__process_mmap,
348 .comm = perf_event__process_comm,
Arnaldo Carvalho de Melof62d3f02012-10-06 15:44:59 -0300349 .exit = perf_event__process_exit,
350 .fork = perf_event__process_fork,
Jiri Olsa863e4512012-09-06 17:46:55 +0200351 .lost = perf_event__process_lost,
352 .ordered_samples = true,
353 .ordering_requires_timestamps = true,
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200354};
355
Jiri Olsa863e4512012-09-06 17:46:55 +0200356static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
357 struct perf_evlist *evlist)
358{
359 struct perf_evsel *e;
360
361 list_for_each_entry(e, &evlist->entries, node)
362 if (perf_evsel__match2(evsel, e))
363 return e;
364
365 return NULL;
366}
367
Namhyung Kimce74f602012-12-10 17:29:55 +0900368static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
Jiri Olsadd464342012-10-04 21:49:36 +0900369{
370 struct perf_evsel *evsel;
371
372 list_for_each_entry(evsel, &evlist->entries, node) {
373 struct hists *hists = &evsel->hists;
374
Namhyung Kimce74f602012-12-10 17:29:55 +0900375 hists__collapse_resort(hists);
Jiri Olsadd464342012-10-04 21:49:36 +0900376 }
377}
378
Jiri Olsaa06d1432012-10-05 16:44:40 +0200379static void hists__baseline_only(struct hists *hists)
380{
Namhyung Kimce74f602012-12-10 17:29:55 +0900381 struct rb_root *root;
382 struct rb_node *next;
Jiri Olsaa06d1432012-10-05 16:44:40 +0200383
Namhyung Kimce74f602012-12-10 17:29:55 +0900384 if (sort__need_collapse)
385 root = &hists->entries_collapsed;
386 else
387 root = hists->entries_in;
388
389 next = rb_first(root);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200390 while (next != NULL) {
Namhyung Kimce74f602012-12-10 17:29:55 +0900391 struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200392
Namhyung Kimce74f602012-12-10 17:29:55 +0900393 next = rb_next(&he->rb_node_in);
Arnaldo Carvalho de Melob821c732012-10-25 14:42:45 -0200394 if (!hist_entry__next_pair(he)) {
Namhyung Kimce74f602012-12-10 17:29:55 +0900395 rb_erase(&he->rb_node_in, root);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200396 hist_entry__free(he);
397 }
398 }
399}
400
Jiri Olsa96c47f12012-10-05 16:44:42 +0200401static void hists__precompute(struct hists *hists)
402{
Jiri Olsa367c53c2012-12-13 14:08:59 +0100403 struct rb_root *root;
404 struct rb_node *next;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200405
Jiri Olsa367c53c2012-12-13 14:08:59 +0100406 if (sort__need_collapse)
407 root = &hists->entries_collapsed;
408 else
409 root = hists->entries_in;
410
411 next = rb_first(root);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200412 while (next != NULL) {
Jiri Olsa367c53c2012-12-13 14:08:59 +0100413 struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
Jiri Olsa05472da2012-11-28 14:52:40 +0100414 struct hist_entry *pair = hist_entry__next_pair(he);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200415
Jiri Olsa367c53c2012-12-13 14:08:59 +0100416 next = rb_next(&he->rb_node_in);
Jiri Olsa05472da2012-11-28 14:52:40 +0100417 if (!pair)
418 continue;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200419
420 switch (compute) {
421 case COMPUTE_DELTA:
Jiri Olsa05472da2012-11-28 14:52:40 +0100422 perf_diff__compute_delta(he, pair);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200423 break;
424 case COMPUTE_RATIO:
Jiri Olsa05472da2012-11-28 14:52:40 +0100425 perf_diff__compute_ratio(he, pair);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200426 break;
Jiri Olsa81d5f952012-10-05 16:44:43 +0200427 case COMPUTE_WEIGHTED_DIFF:
Jiri Olsa05472da2012-11-28 14:52:40 +0100428 perf_diff__compute_wdiff(he, pair);
Jiri Olsa81d5f952012-10-05 16:44:43 +0200429 break;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200430 default:
431 BUG_ON(1);
432 }
433 }
434}
435
436static int64_t cmp_doubles(double l, double r)
437{
438 if (l > r)
439 return -1;
440 else if (l < r)
441 return 1;
442 else
443 return 0;
444}
445
446static int64_t
447hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
448 int c)
449{
450 switch (c) {
451 case COMPUTE_DELTA:
452 {
453 double l = left->diff.period_ratio_delta;
454 double r = right->diff.period_ratio_delta;
455
456 return cmp_doubles(l, r);
457 }
458 case COMPUTE_RATIO:
459 {
460 double l = left->diff.period_ratio;
461 double r = right->diff.period_ratio;
462
463 return cmp_doubles(l, r);
464 }
Jiri Olsa81d5f952012-10-05 16:44:43 +0200465 case COMPUTE_WEIGHTED_DIFF:
466 {
467 s64 l = left->diff.wdiff;
468 s64 r = right->diff.wdiff;
469
470 return r - l;
471 }
Jiri Olsa96c47f12012-10-05 16:44:42 +0200472 default:
473 BUG_ON(1);
474 }
475
476 return 0;
477}
478
479static void insert_hist_entry_by_compute(struct rb_root *root,
480 struct hist_entry *he,
481 int c)
482{
483 struct rb_node **p = &root->rb_node;
484 struct rb_node *parent = NULL;
485 struct hist_entry *iter;
486
487 while (*p != NULL) {
488 parent = *p;
489 iter = rb_entry(parent, struct hist_entry, rb_node);
490 if (hist_entry__cmp_compute(he, iter, c) < 0)
491 p = &(*p)->rb_left;
492 else
493 p = &(*p)->rb_right;
494 }
495
496 rb_link_node(&he->rb_node, parent, p);
497 rb_insert_color(&he->rb_node, root);
498}
499
500static void hists__compute_resort(struct hists *hists)
501{
Namhyung Kim66f97ed2012-12-10 17:29:56 +0900502 struct rb_root *root;
503 struct rb_node *next;
504
505 if (sort__need_collapse)
506 root = &hists->entries_collapsed;
507 else
508 root = hists->entries_in;
509
510 hists->entries = RB_ROOT;
511 next = rb_first(root);
512
513 hists->nr_entries = 0;
514 hists->stats.total_period = 0;
515 hists__reset_col_len(hists);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200516
517 while (next != NULL) {
Namhyung Kim66f97ed2012-12-10 17:29:56 +0900518 struct hist_entry *he;
Jiri Olsa96c47f12012-10-05 16:44:42 +0200519
Namhyung Kim66f97ed2012-12-10 17:29:56 +0900520 he = rb_entry(next, struct hist_entry, rb_node_in);
521 next = rb_next(&he->rb_node_in);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200522
Namhyung Kim66f97ed2012-12-10 17:29:56 +0900523 insert_hist_entry_by_compute(&hists->entries, he, compute);
524 hists__inc_nr_entries(hists, he);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200525 }
Jiri Olsa96c47f12012-10-05 16:44:42 +0200526}
527
Jiri Olsa9af303e2012-12-01 21:15:40 +0100528static void hists__process(struct hists *base, struct hists *new)
Jiri Olsaa06d1432012-10-05 16:44:40 +0200529{
Jiri Olsa9af303e2012-12-01 21:15:40 +0100530 hists__match(base, new);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200531
532 if (show_baseline_only)
Jiri Olsa9af303e2012-12-01 21:15:40 +0100533 hists__baseline_only(base);
Arnaldo Carvalho de Melobfaef4b2012-11-08 18:08:26 -0300534 else
Jiri Olsa9af303e2012-12-01 21:15:40 +0100535 hists__link(base, new);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200536
Jiri Olsa96c47f12012-10-05 16:44:42 +0200537 if (sort_compute) {
Jiri Olsa9af303e2012-12-01 21:15:40 +0100538 hists__precompute(base);
539 hists__compute_resort(base);
Namhyung Kim66f97ed2012-12-10 17:29:56 +0900540 } else {
Jiri Olsa9af303e2012-12-01 21:15:40 +0100541 hists__output_resort(base);
Jiri Olsa96c47f12012-10-05 16:44:42 +0200542 }
543
Jiri Olsa9af303e2012-12-01 21:15:40 +0100544 hists__fprintf(base, true, 0, 0, 0, stdout);
Jiri Olsaa06d1432012-10-05 16:44:40 +0200545}
546
Jiri Olsa1d81c7f2012-12-01 21:56:03 +0100547static void data__fprintf(void)
548{
549 struct data__file *d;
550 int i;
551
552 fprintf(stdout, "# Data files:\n");
553
554 data__for_each_file(i, d)
555 fprintf(stdout, "# [%d] %s %s\n",
556 d->idx, d->file,
557 !d->idx ? "(Baseline)" : "");
558
559 fprintf(stdout, "#\n");
560}
561
Jiri Olsaec308422013-03-25 00:02:01 +0100562static void data_process(void)
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200563{
Jiri Olsaec308422013-03-25 00:02:01 +0100564 struct perf_evlist *evlist_old = data__files[0].session->evlist;
565 struct perf_evlist *evlist_new = data__files[1].session->evlist;
566 struct perf_evsel *evsel_old;
Jiri Olsa863e4512012-09-06 17:46:55 +0200567 bool first = true;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200568
Jiri Olsaec308422013-03-25 00:02:01 +0100569 list_for_each_entry(evsel_old, &evlist_old->entries, node) {
570 struct perf_evsel *evsel_new;
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200571
Jiri Olsaec308422013-03-25 00:02:01 +0100572 evsel_new = evsel_match(evsel_old, evlist_new);
573 if (!evsel_new)
Jiri Olsa863e4512012-09-06 17:46:55 +0200574 continue;
575
576 fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
Jiri Olsaec308422013-03-25 00:02:01 +0100577 perf_evsel__name(evsel_old));
Jiri Olsa863e4512012-09-06 17:46:55 +0200578
579 first = false;
580
Jiri Olsa1d81c7f2012-12-01 21:56:03 +0100581 if (verbose)
582 data__fprintf();
583
Jiri Olsaec308422013-03-25 00:02:01 +0100584 hists__process(&evsel_old->hists, &evsel_new->hists);
585 }
586}
587
Jiri Olsac818b492012-12-01 21:57:04 +0100588static void data__free(struct data__file *d)
589{
590 int col;
591
592 for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
593 struct diff_hpp_fmt *fmt = &d->fmt[col];
594
595 free(fmt->header);
596 }
597}
598
Jiri Olsaec308422013-03-25 00:02:01 +0100599static int __cmd_diff(void)
600{
601 struct data__file *d;
602 int ret = -EINVAL, i;
603
604 data__for_each_file(i, d) {
605 d->session = perf_session__new(d->file, O_RDONLY, force,
606 false, &tool);
607 if (!d->session) {
608 pr_err("Failed to open %s\n", d->file);
609 ret = -ENOMEM;
610 goto out_delete;
611 }
612
613 ret = perf_session__process_events(d->session, &tool);
614 if (ret) {
615 pr_err("Failed to process %s\n", d->file);
616 goto out_delete;
617 }
618
619 perf_evlist__collapse_resort(d->session->evlist);
Jiri Olsa863e4512012-09-06 17:46:55 +0200620 }
621
Jiri Olsaec308422013-03-25 00:02:01 +0100622 data_process();
623
624 out_delete:
625 data__for_each_file(i, d) {
626 if (d->session)
627 perf_session__delete(d->session);
Jiri Olsac818b492012-12-01 21:57:04 +0100628
629 data__free(d);
Jiri Olsaec308422013-03-25 00:02:01 +0100630 }
631
632 free(data__files);
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200633 return ret;
634}
635
Arnaldo Carvalho de Melo0422a4f2009-12-18 16:35:58 -0200636static const char * const diff_usage[] = {
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200637 "perf diff [<options>] [old_file] [new_file]",
Arnaldo Carvalho de Melo0422a4f2009-12-18 16:35:58 -0200638 NULL,
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200639};
640
641static const struct option options[] = {
Ian Munsiec0555642010-04-13 18:37:33 +1000642 OPT_INCR('v', "verbose", &verbose,
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200643 "be more verbose (show symbol address, etc)"),
Jiri Olsaa06d1432012-10-05 16:44:40 +0200644 OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
645 "Show only items with match in baseline"),
Jiri Olsa81d5f952012-10-05 16:44:43 +0200646 OPT_CALLBACK('c', "compute", &compute,
647 "delta,ratio,wdiff:w1,w2 (default delta)",
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200648 "Entries differential computation selection",
649 setup_compute),
Jiri Olsa61949b22012-10-05 16:44:44 +0200650 OPT_BOOLEAN('p', "period", &show_period,
651 "Show period values."),
Jiri Olsaed279da2012-10-05 16:44:45 +0200652 OPT_BOOLEAN('F', "formula", &show_formula,
653 "Show formula."),
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200654 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
655 "dump raw trace in ASCII"),
656 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
657 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
658 "load module symbols - WARNING: use only with -k and LIVE kernel"),
Arnaldo Carvalho de Meloc410a332009-12-15 20:04:41 -0200659 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
660 "only consider symbols in these dsos"),
661 OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
662 "only consider symbols in these comms"),
663 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
664 "only consider these symbols"),
Arnaldo Carvalho de Meloc351c282009-12-16 13:49:27 -0200665 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
666 "sort by key(s): pid, comm, dso, symbol, parent"),
667 OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
668 "separator for columns, no spaces will be added between "
669 "columns '.' is reserved."),
David Ahernec5761e2010-12-09 13:27:07 -0700670 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
671 "Look for files with symbols relative to this directory"),
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200672 OPT_END()
673};
674
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100675static double baseline_percent(struct hist_entry *he)
Jiri Olsa1d778222012-10-04 21:49:39 +0900676{
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100677 struct hists *hists = he->hists;
678 return 100.0 * he->stat.period / hists->stats.total_period;
679}
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200680
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100681static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
682 struct perf_hpp *hpp, struct hist_entry *he)
683{
684 struct diff_hpp_fmt *dfmt =
685 container_of(fmt, struct diff_hpp_fmt, fmt);
686 double percent = baseline_percent(he);
687 char pfmt[20] = " ";
688
689 if (!he->dummy) {
690 scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1);
691 return percent_color_snprintf(hpp->buf, hpp->size,
692 pfmt, percent);
693 } else
694 return scnprintf(hpp->buf, hpp->size, "%*s",
695 dfmt->header_width, pfmt);
696}
697
698static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
699{
700 double percent = baseline_percent(he);
701 const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
702 int ret = 0;
703
704 if (!he->dummy)
705 ret = scnprintf(buf, size, fmt, percent);
706
707 return ret;
708}
709
710static void
711hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
712{
713 switch (idx) {
714 case PERF_HPP_DIFF__PERIOD_BASELINE:
715 scnprintf(buf, size, "%" PRIu64, he->stat.period);
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200716 break;
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100717
718 default:
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200719 break;
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100720 }
721}
722
723static void
724hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
725 int idx, char *buf, size_t size)
726{
727 double diff;
728 double ratio;
729 s64 wdiff;
730
731 switch (idx) {
732 case PERF_HPP_DIFF__DELTA:
733 if (pair->diff.computed)
734 diff = pair->diff.period_ratio_delta;
735 else
736 diff = perf_diff__compute_delta(he, pair);
737
738 if (fabs(diff) >= 0.01)
739 scnprintf(buf, size, "%+4.2F%%", diff);
Jiri Olsa81d5f952012-10-05 16:44:43 +0200740 break;
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100741
742 case PERF_HPP_DIFF__RATIO:
743 /* No point for ratio number if we are dummy.. */
744 if (he->dummy)
745 break;
746
747 if (pair->diff.computed)
748 ratio = pair->diff.period_ratio;
749 else
750 ratio = perf_diff__compute_ratio(he, pair);
751
752 if (ratio > 0.0)
753 scnprintf(buf, size, "%14.6F", ratio);
754 break;
755
756 case PERF_HPP_DIFF__WEIGHTED_DIFF:
757 /* No point for wdiff number if we are dummy.. */
758 if (he->dummy)
759 break;
760
761 if (pair->diff.computed)
762 wdiff = pair->diff.wdiff;
763 else
764 wdiff = perf_diff__compute_wdiff(he, pair);
765
766 if (wdiff != 0)
767 scnprintf(buf, size, "%14ld", wdiff);
768 break;
769
770 case PERF_HPP_DIFF__FORMULA:
771 perf_diff__formula(he, pair, buf, size);
772 break;
773
774 case PERF_HPP_DIFF__PERIOD:
775 scnprintf(buf, size, "%" PRIu64, pair->stat.period);
776 break;
777
Jiri Olsa7aaf6b32012-10-05 16:44:41 +0200778 default:
779 BUG_ON(1);
780 };
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100781}
782
783static void
784__hpp__entry_global(struct hist_entry *he, int idx, char *buf, size_t size)
785{
786 struct hist_entry *pair = hist_entry__next_pair(he);
787
788 /* baseline is special */
789 if (idx == PERF_HPP_DIFF__BASELINE)
790 hpp__entry_baseline(he, buf, size);
791 else {
792 if (pair)
793 hpp__entry_pair(he, pair, idx, buf, size);
794 else
795 hpp__entry_unpair(he, idx, buf, size);
796 }
797}
798
799static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,
800 struct hist_entry *he)
801{
802 struct diff_hpp_fmt *dfmt =
803 container_of(_fmt, struct diff_hpp_fmt, fmt);
804 char buf[MAX_COL_WIDTH] = " ";
805
806 __hpp__entry_global(he, dfmt->idx, buf, MAX_COL_WIDTH);
807
808 if (symbol_conf.field_sep)
809 return scnprintf(hpp->buf, hpp->size, "%s", buf);
810 else
811 return scnprintf(hpp->buf, hpp->size, "%*s",
812 dfmt->header_width, buf);
813}
814
815static int hpp__header(struct perf_hpp_fmt *fmt,
816 struct perf_hpp *hpp)
817{
818 struct diff_hpp_fmt *dfmt =
819 container_of(fmt, struct diff_hpp_fmt, fmt);
820
821 BUG_ON(!dfmt->header);
822 return scnprintf(hpp->buf, hpp->size, dfmt->header);
823}
824
825static int hpp__width(struct perf_hpp_fmt *fmt,
826 struct perf_hpp *hpp __maybe_unused)
827{
828 struct diff_hpp_fmt *dfmt =
829 container_of(fmt, struct diff_hpp_fmt, fmt);
830
831 BUG_ON(dfmt->header_width <= 0);
832 return dfmt->header_width;
833}
834
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100835static void init_header(struct diff_hpp_fmt *dfmt)
836{
837#define MAX_HEADER_NAME 100
838 char buf_indent[MAX_HEADER_NAME];
839 char buf[MAX_HEADER_NAME];
840 const char *header = NULL;
841 int width = 0;
842
843 BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX);
844 header = columns[dfmt->idx].name;
845 width = columns[dfmt->idx].width;
846
847 /* Only our defined HPP fmts should appear here. */
848 BUG_ON(!header);
849
850#define NAME (data__files_cnt > 2 ? buf : header)
851 dfmt->header_width = width;
852 width = (int) strlen(NAME);
853 if (dfmt->header_width < width)
854 dfmt->header_width = width;
855
856 scnprintf(buf_indent, MAX_HEADER_NAME, "%*s",
857 dfmt->header_width, NAME);
858
859 dfmt->header = strdup(buf_indent);
860#undef MAX_HEADER_NAME
861#undef NAME
862}
863
Jiri Olsac818b492012-12-01 21:57:04 +0100864static void data__hpp_register(struct data__file *d, int idx)
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100865{
Jiri Olsac818b492012-12-01 21:57:04 +0100866 struct diff_hpp_fmt *dfmt = &d->fmt[idx];
867 struct perf_hpp_fmt *fmt = &dfmt->fmt;
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100868
Jiri Olsac818b492012-12-01 21:57:04 +0100869 dfmt->idx = idx;
870
871 fmt->header = hpp__header;
872 fmt->width = hpp__width;
873 fmt->entry = hpp__entry_global;
874
875 /* TODO more colors */
876 if (idx == PERF_HPP_DIFF__BASELINE)
877 fmt->color = hpp__color_baseline;
878
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100879 init_header(dfmt);
Jiri Olsac818b492012-12-01 21:57:04 +0100880 perf_hpp__column_register(fmt);
Jiri Olsa345dc0b2013-02-03 20:08:34 +0100881}
882
883static void ui_init(void)
884{
Jiri Olsac818b492012-12-01 21:57:04 +0100885 struct data__file *d;
886 int i;
Jiri Olsa1d778222012-10-04 21:49:39 +0900887
Jiri Olsac818b492012-12-01 21:57:04 +0100888 data__for_each_file(i, d) {
Jiri Olsaed279da2012-10-05 16:44:45 +0200889
Jiri Olsac818b492012-12-01 21:57:04 +0100890 /*
891 * Baseline or compute realted columns:
892 *
893 * PERF_HPP_DIFF__BASELINE
894 * PERF_HPP_DIFF__DELTA
895 * PERF_HPP_DIFF__RATIO
896 * PERF_HPP_DIFF__WEIGHTED_DIFF
897 */
898 data__hpp_register(d, i ? compute_2_hpp[compute] :
899 PERF_HPP_DIFF__BASELINE);
900
901 /*
902 * And the rest:
903 *
904 * PERF_HPP_DIFF__FORMULA
905 * PERF_HPP_DIFF__PERIOD
906 * PERF_HPP_DIFF__PERIOD_BASELINE
907 */
908 if (show_formula && i)
909 data__hpp_register(d, PERF_HPP_DIFF__FORMULA);
910
911 if (show_period)
912 data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
913 PERF_HPP_DIFF__PERIOD_BASELINE);
Jiri Olsa61949b22012-10-05 16:44:44 +0200914 }
Jiri Olsa1d778222012-10-04 21:49:39 +0900915}
916
Jiri Olsaec308422013-03-25 00:02:01 +0100917static int data_init(int argc, const char **argv)
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200918{
Jiri Olsaec308422013-03-25 00:02:01 +0100919 struct data__file *d;
920 static const char *defaults[] = {
921 "perf.data.old",
922 "perf.data",
923 };
924 int i;
925
926 data__files_cnt = 2;
927
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200928 if (argc) {
929 if (argc > 2)
930 usage_with_options(diff_usage, options);
931 if (argc == 2) {
Jiri Olsaec308422013-03-25 00:02:01 +0100932 defaults[0] = argv[0];
933 defaults[1] = argv[1];
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200934 } else
Jiri Olsaec308422013-03-25 00:02:01 +0100935 defaults[1] = argv[0];
Zhang, Yanmina1645ce2010-04-19 13:32:50 +0800936 } else if (symbol_conf.default_guest_vmlinux_name ||
937 symbol_conf.default_guest_kallsyms) {
Jiri Olsaec308422013-03-25 00:02:01 +0100938 defaults[0] = "perf.data.host";
939 defaults[1] = "perf.data.guest";
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200940 }
941
Jiri Olsaec308422013-03-25 00:02:01 +0100942 data__files = zalloc(sizeof(*data__files) * data__files_cnt);
943 if (!data__files)
944 return -ENOMEM;
945
946 data__for_each_file(i, d) {
947 d->file = defaults[i];
948 d->idx = i;
949 }
950
951 return 0;
952}
953
954int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
955{
956 sort_order = diff__default_sort_order;
957 argc = parse_options(argc, argv, options, diff_usage, 0);
958
Arnaldo Carvalho de Melo655000e2009-12-15 20:04:40 -0200959 if (symbol__init() < 0)
960 return -1;
961
Jiri Olsaec308422013-03-25 00:02:01 +0100962 if (data_init(argc, argv) < 0)
963 return -1;
964
Jiri Olsa1d778222012-10-04 21:49:39 +0900965 ui_init();
966
Namhyung Kim55309982013-02-06 14:57:16 +0900967 if (setup_sorting() < 0)
968 usage_with_options(diff_usage, options);
969
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200970 setup_pager();
Arnaldo Carvalho de Meloc351c282009-12-16 13:49:27 -0200971
Namhyung Kim08e71542013-04-03 21:26:19 +0900972 sort__setup_elide(NULL);
Arnaldo Carvalho de Meloc351c282009-12-16 13:49:27 -0200973
Arnaldo Carvalho de Melo86a9eee2009-12-14 20:09:31 -0200974 return __cmd_diff();
975}