Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/perf/builtin-diff.c
26282 views
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
#include "perf.h"
10
11
#include "util/debug.h"
12
#include "util/event.h"
13
#include "util/hist.h"
14
#include "util/evsel.h"
15
#include "util/evlist.h"
16
#include "util/session.h"
17
#include "util/tool.h"
18
#include "util/sort.h"
19
#include "util/srcline.h"
20
#include "util/symbol.h"
21
#include "util/data.h"
22
#include "util/config.h"
23
#include "util/time-utils.h"
24
#include "util/annotate.h"
25
#include "util/map.h"
26
#include "util/spark.h"
27
#include "util/block-info.h"
28
#include "util/stream.h"
29
#include "util/util.h"
30
#include <linux/err.h>
31
#include <linux/zalloc.h>
32
#include <subcmd/pager.h>
33
#include <subcmd/parse-options.h>
34
35
#include <errno.h>
36
#include <inttypes.h>
37
#include <stdlib.h>
38
#include <math.h>
39
40
struct perf_diff {
41
struct perf_tool tool;
42
const char *time_str;
43
struct perf_time_interval *ptime_range;
44
int range_size;
45
int range_num;
46
bool has_br_stack;
47
bool stream;
48
};
49
50
/* Diff command specific HPP columns. */
51
enum {
52
PERF_HPP_DIFF__BASELINE,
53
PERF_HPP_DIFF__PERIOD,
54
PERF_HPP_DIFF__PERIOD_BASELINE,
55
PERF_HPP_DIFF__DELTA,
56
PERF_HPP_DIFF__RATIO,
57
PERF_HPP_DIFF__WEIGHTED_DIFF,
58
PERF_HPP_DIFF__FORMULA,
59
PERF_HPP_DIFF__DELTA_ABS,
60
PERF_HPP_DIFF__CYCLES,
61
PERF_HPP_DIFF__CYCLES_HIST,
62
63
PERF_HPP_DIFF__MAX_INDEX
64
};
65
66
struct diff_hpp_fmt {
67
struct perf_hpp_fmt fmt;
68
int idx;
69
char *header;
70
int header_width;
71
};
72
73
struct data__file {
74
struct perf_session *session;
75
struct perf_data data;
76
int idx;
77
struct hists *hists;
78
struct evlist_streams *evlist_streams;
79
struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX];
80
};
81
82
static struct data__file *data__files;
83
static int data__files_cnt;
84
85
#define data__for_each_file_start(i, d, s) \
86
for (i = s, d = &data__files[s]; \
87
i < data__files_cnt; \
88
i++, d = &data__files[i])
89
90
#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
91
#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
92
93
static bool force;
94
static bool show_period;
95
static bool show_formula;
96
static bool show_baseline_only;
97
static bool cycles_hist;
98
static unsigned int sort_compute = 1;
99
100
static s64 compute_wdiff_w1;
101
static s64 compute_wdiff_w2;
102
103
static const char *cpu_list;
104
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
105
106
enum {
107
COMPUTE_DELTA,
108
COMPUTE_RATIO,
109
COMPUTE_WEIGHTED_DIFF,
110
COMPUTE_DELTA_ABS,
111
COMPUTE_CYCLES,
112
COMPUTE_MAX,
113
COMPUTE_STREAM, /* After COMPUTE_MAX to avoid use current compute arrays */
114
};
115
116
const char *compute_names[COMPUTE_MAX] = {
117
[COMPUTE_DELTA] = "delta",
118
[COMPUTE_DELTA_ABS] = "delta-abs",
119
[COMPUTE_RATIO] = "ratio",
120
[COMPUTE_WEIGHTED_DIFF] = "wdiff",
121
[COMPUTE_CYCLES] = "cycles",
122
};
123
124
static int compute = COMPUTE_DELTA_ABS;
125
126
static int compute_2_hpp[COMPUTE_MAX] = {
127
[COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA,
128
[COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS,
129
[COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
130
[COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
131
[COMPUTE_CYCLES] = PERF_HPP_DIFF__CYCLES,
132
};
133
134
#define MAX_COL_WIDTH 70
135
136
static struct header_column {
137
const char *name;
138
int width;
139
} columns[PERF_HPP_DIFF__MAX_INDEX] = {
140
[PERF_HPP_DIFF__BASELINE] = {
141
.name = "Baseline",
142
},
143
[PERF_HPP_DIFF__PERIOD] = {
144
.name = "Period",
145
.width = 14,
146
},
147
[PERF_HPP_DIFF__PERIOD_BASELINE] = {
148
.name = "Base period",
149
.width = 14,
150
},
151
[PERF_HPP_DIFF__DELTA] = {
152
.name = "Delta",
153
.width = 7,
154
},
155
[PERF_HPP_DIFF__DELTA_ABS] = {
156
.name = "Delta Abs",
157
.width = 7,
158
},
159
[PERF_HPP_DIFF__RATIO] = {
160
.name = "Ratio",
161
.width = 14,
162
},
163
[PERF_HPP_DIFF__WEIGHTED_DIFF] = {
164
.name = "Weighted diff",
165
.width = 14,
166
},
167
[PERF_HPP_DIFF__FORMULA] = {
168
.name = "Formula",
169
.width = MAX_COL_WIDTH,
170
},
171
[PERF_HPP_DIFF__CYCLES] = {
172
.name = "[Program Block Range] Cycles Diff",
173
.width = 70,
174
},
175
[PERF_HPP_DIFF__CYCLES_HIST] = {
176
.name = "stddev/Hist",
177
.width = NUM_SPARKS + 9,
178
}
179
};
180
181
static int setup_compute_opt_wdiff(char *opt)
182
{
183
char *w1_str = opt;
184
char *w2_str;
185
186
int ret = -EINVAL;
187
188
if (!opt)
189
goto out;
190
191
w2_str = strchr(opt, ',');
192
if (!w2_str)
193
goto out;
194
195
*w2_str++ = 0x0;
196
if (!*w2_str)
197
goto out;
198
199
compute_wdiff_w1 = strtol(w1_str, NULL, 10);
200
compute_wdiff_w2 = strtol(w2_str, NULL, 10);
201
202
if (!compute_wdiff_w1 || !compute_wdiff_w2)
203
goto out;
204
205
pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
206
compute_wdiff_w1, compute_wdiff_w2);
207
208
ret = 0;
209
210
out:
211
if (ret)
212
pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
213
214
return ret;
215
}
216
217
static int setup_compute_opt(char *opt)
218
{
219
if (compute == COMPUTE_WEIGHTED_DIFF)
220
return setup_compute_opt_wdiff(opt);
221
222
if (opt) {
223
pr_err("Failed: extra option specified '%s'", opt);
224
return -EINVAL;
225
}
226
227
return 0;
228
}
229
230
static int setup_compute(const struct option *opt, const char *str,
231
int unset __maybe_unused)
232
{
233
int *cp = (int *) opt->value;
234
char *cstr = (char *) str;
235
char buf[50];
236
unsigned i;
237
char *option;
238
239
if (!str) {
240
*cp = COMPUTE_DELTA;
241
return 0;
242
}
243
244
option = strchr(str, ':');
245
if (option) {
246
unsigned len = option++ - str;
247
248
/*
249
* The str data are not writeable, so we need
250
* to use another buffer.
251
*/
252
253
/* No option value is longer. */
254
if (len >= sizeof(buf))
255
return -EINVAL;
256
257
strncpy(buf, str, len);
258
buf[len] = 0x0;
259
cstr = buf;
260
}
261
262
for (i = 0; i < COMPUTE_MAX; i++)
263
if (!strcmp(cstr, compute_names[i])) {
264
*cp = i;
265
return setup_compute_opt(option);
266
}
267
268
pr_err("Failed: '%s' is not computation method "
269
"(use 'delta','ratio' or 'wdiff')\n", str);
270
return -EINVAL;
271
}
272
273
static double period_percent(struct hist_entry *he, u64 period)
274
{
275
u64 total = hists__total_period(he->hists);
276
277
return (period * 100.0) / total;
278
}
279
280
static double compute_delta(struct hist_entry *he, struct hist_entry *pair)
281
{
282
double old_percent = period_percent(he, he->stat.period);
283
double new_percent = period_percent(pair, pair->stat.period);
284
285
pair->diff.period_ratio_delta = new_percent - old_percent;
286
pair->diff.computed = true;
287
return pair->diff.period_ratio_delta;
288
}
289
290
static double compute_ratio(struct hist_entry *he, struct hist_entry *pair)
291
{
292
double old_period = he->stat.period ?: 1;
293
double new_period = pair->stat.period;
294
295
pair->diff.computed = true;
296
pair->diff.period_ratio = new_period / old_period;
297
return pair->diff.period_ratio;
298
}
299
300
static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
301
{
302
u64 old_period = he->stat.period;
303
u64 new_period = pair->stat.period;
304
305
pair->diff.computed = true;
306
pair->diff.wdiff = new_period * compute_wdiff_w2 -
307
old_period * compute_wdiff_w1;
308
309
return pair->diff.wdiff;
310
}
311
312
static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
313
char *buf, size_t size)
314
{
315
u64 he_total = he->hists->stats.total_period;
316
u64 pair_total = pair->hists->stats.total_period;
317
318
if (symbol_conf.filter_relative) {
319
he_total = he->hists->stats.total_non_filtered_period;
320
pair_total = pair->hists->stats.total_non_filtered_period;
321
}
322
return scnprintf(buf, size,
323
"(%" PRIu64 " * 100 / %" PRIu64 ") - "
324
"(%" PRIu64 " * 100 / %" PRIu64 ")",
325
pair->stat.period, pair_total,
326
he->stat.period, he_total);
327
}
328
329
static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
330
char *buf, size_t size)
331
{
332
double old_period = he->stat.period;
333
double new_period = pair->stat.period;
334
335
return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
336
}
337
338
static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair,
339
char *buf, size_t size)
340
{
341
u64 old_period = he->stat.period;
342
u64 new_period = pair->stat.period;
343
344
return scnprintf(buf, size,
345
"(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
346
new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
347
}
348
349
static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
350
char *buf, size_t size)
351
{
352
switch (compute) {
353
case COMPUTE_DELTA:
354
case COMPUTE_DELTA_ABS:
355
return formula_delta(he, pair, buf, size);
356
case COMPUTE_RATIO:
357
return formula_ratio(he, pair, buf, size);
358
case COMPUTE_WEIGHTED_DIFF:
359
return formula_wdiff(he, pair, buf, size);
360
default:
361
BUG_ON(1);
362
}
363
364
return -1;
365
}
366
367
static void *block_hist_zalloc(size_t size)
368
{
369
struct block_hist *bh;
370
371
bh = zalloc(size + sizeof(*bh));
372
if (!bh)
373
return NULL;
374
375
return &bh->he;
376
}
377
378
static void block_hist_free(void *he)
379
{
380
struct block_hist *bh;
381
382
bh = container_of(he, struct block_hist, he);
383
hists__delete_entries(&bh->block_hists);
384
free(bh);
385
}
386
387
struct hist_entry_ops block_hist_ops = {
388
.new = block_hist_zalloc,
389
.free = block_hist_free,
390
};
391
392
static int diff__process_sample_event(const struct perf_tool *tool,
393
union perf_event *event,
394
struct perf_sample *sample,
395
struct evsel *evsel,
396
struct machine *machine)
397
{
398
struct perf_diff *pdiff = container_of(tool, struct perf_diff, tool);
399
struct addr_location al;
400
struct hists *hists = evsel__hists(evsel);
401
struct hist_entry_iter iter = {
402
.evsel = evsel,
403
.sample = sample,
404
.ops = &hist_iter_normal,
405
};
406
int ret = -1;
407
408
if (perf_time__ranges_skip_sample(pdiff->ptime_range, pdiff->range_num,
409
sample->time)) {
410
return 0;
411
}
412
413
addr_location__init(&al);
414
if (machine__resolve(machine, &al, sample) < 0) {
415
pr_warning("problem processing %d event, skipping it.\n",
416
event->header.type);
417
ret = -1;
418
goto out;
419
}
420
421
if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) {
422
ret = 0;
423
goto out;
424
}
425
426
switch (compute) {
427
case COMPUTE_CYCLES:
428
if (!hists__add_entry_ops(hists, &block_hist_ops, &al, NULL,
429
NULL, NULL, NULL, sample, true)) {
430
pr_warning("problem incrementing symbol period, "
431
"skipping event\n");
432
goto out;
433
}
434
435
hist__account_cycles(sample->branch_stack, &al, sample,
436
false, NULL, evsel);
437
break;
438
439
case COMPUTE_STREAM:
440
if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
441
NULL)) {
442
pr_debug("problem adding hist entry, skipping event\n");
443
goto out;
444
}
445
break;
446
447
default:
448
if (!hists__add_entry(hists, &al, NULL, NULL, NULL, NULL, sample,
449
true)) {
450
pr_warning("problem incrementing symbol period, "
451
"skipping event\n");
452
goto out;
453
}
454
}
455
456
/*
457
* The total_period is updated here before going to the output
458
* tree since normally only the baseline hists will call
459
* hists__output_resort() and precompute needs the total
460
* period in order to sort entries by percentage delta.
461
*/
462
hists->stats.total_period += sample->period;
463
if (!al.filtered)
464
hists->stats.total_non_filtered_period += sample->period;
465
ret = 0;
466
out:
467
addr_location__exit(&al);
468
return ret;
469
}
470
471
static struct perf_diff pdiff;
472
473
static struct evsel *evsel_match(struct evsel *evsel, struct evlist *evlist)
474
{
475
struct evsel *e;
476
477
evlist__for_each_entry(evlist, e) {
478
if ((evsel->core.attr.type == e->core.attr.type) &&
479
(evsel->core.attr.config == e->core.attr.config))
480
return e;
481
}
482
483
return NULL;
484
}
485
486
static void evlist__collapse_resort(struct evlist *evlist)
487
{
488
struct evsel *evsel;
489
490
evlist__for_each_entry(evlist, evsel) {
491
struct hists *hists = evsel__hists(evsel);
492
493
hists__collapse_resort(hists, NULL);
494
}
495
}
496
497
static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt)
498
{
499
struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt);
500
void *ptr = dfmt - dfmt->idx;
501
struct data__file *d = container_of(ptr, struct data__file, fmt);
502
503
return d;
504
}
505
506
static struct hist_entry*
507
get_pair_data(struct hist_entry *he, struct data__file *d)
508
{
509
if (hist_entry__has_pairs(he)) {
510
struct hist_entry *pair;
511
512
list_for_each_entry(pair, &he->pairs.head, pairs.node)
513
if (pair->hists == d->hists)
514
return pair;
515
}
516
517
return NULL;
518
}
519
520
static struct hist_entry*
521
get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt)
522
{
523
struct data__file *d = fmt_to_data_file(&dfmt->fmt);
524
525
return get_pair_data(he, d);
526
}
527
528
static void hists__baseline_only(struct hists *hists)
529
{
530
struct rb_root_cached *root;
531
struct rb_node *next;
532
533
if (hists__has(hists, need_collapse))
534
root = &hists->entries_collapsed;
535
else
536
root = hists->entries_in;
537
538
next = rb_first_cached(root);
539
while (next != NULL) {
540
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
541
542
next = rb_next(&he->rb_node_in);
543
if (!hist_entry__next_pair(he)) {
544
rb_erase_cached(&he->rb_node_in, root);
545
hist_entry__delete(he);
546
}
547
}
548
}
549
550
static int64_t block_cycles_diff_cmp(struct hist_entry *left,
551
struct hist_entry *right)
552
{
553
bool pairs_left = hist_entry__has_pairs(left);
554
bool pairs_right = hist_entry__has_pairs(right);
555
s64 l, r;
556
557
if (!pairs_left && !pairs_right)
558
return 0;
559
560
l = llabs(left->diff.cycles);
561
r = llabs(right->diff.cycles);
562
return r - l;
563
}
564
565
static int64_t block_sort(struct perf_hpp_fmt *fmt __maybe_unused,
566
struct hist_entry *left, struct hist_entry *right)
567
{
568
return block_cycles_diff_cmp(right, left);
569
}
570
571
static void init_block_hist(struct block_hist *bh)
572
{
573
__hists__init(&bh->block_hists, &bh->block_list);
574
perf_hpp_list__init(&bh->block_list);
575
576
INIT_LIST_HEAD(&bh->block_fmt.list);
577
INIT_LIST_HEAD(&bh->block_fmt.sort_list);
578
bh->block_fmt.cmp = block_info__cmp;
579
bh->block_fmt.sort = block_sort;
580
perf_hpp_list__register_sort_field(&bh->block_list,
581
&bh->block_fmt);
582
bh->valid = true;
583
}
584
585
static struct hist_entry *get_block_pair(struct hist_entry *he,
586
struct hists *hists_pair)
587
{
588
struct rb_root_cached *root = hists_pair->entries_in;
589
struct rb_node *next = rb_first_cached(root);
590
int64_t cmp;
591
592
while (next != NULL) {
593
struct hist_entry *he_pair = rb_entry(next, struct hist_entry,
594
rb_node_in);
595
596
next = rb_next(&he_pair->rb_node_in);
597
598
cmp = __block_info__cmp(he_pair, he);
599
if (!cmp)
600
return he_pair;
601
}
602
603
return NULL;
604
}
605
606
static void init_spark_values(unsigned long *svals, int num)
607
{
608
for (int i = 0; i < num; i++)
609
svals[i] = 0;
610
}
611
612
static void update_spark_value(unsigned long *svals, int num,
613
struct stats *stats, u64 val)
614
{
615
int n = stats->n;
616
617
if (n < num)
618
svals[n] = val;
619
}
620
621
static void compute_cycles_diff(struct hist_entry *he,
622
struct hist_entry *pair)
623
{
624
pair->diff.computed = true;
625
if (pair->block_info->num && he->block_info->num) {
626
pair->diff.cycles =
627
pair->block_info->cycles_aggr / pair->block_info->num_aggr -
628
he->block_info->cycles_aggr / he->block_info->num_aggr;
629
630
if (!cycles_hist)
631
return;
632
633
init_stats(&pair->diff.stats);
634
init_spark_values(pair->diff.svals, NUM_SPARKS);
635
636
for (int i = 0; i < pair->block_info->num; i++) {
637
u64 val;
638
639
if (i >= he->block_info->num || i >= NUM_SPARKS)
640
break;
641
642
val = llabs(pair->block_info->cycles_spark[i] -
643
he->block_info->cycles_spark[i]);
644
645
update_spark_value(pair->diff.svals, NUM_SPARKS,
646
&pair->diff.stats, val);
647
update_stats(&pair->diff.stats, val);
648
}
649
}
650
}
651
652
static void block_hists_match(struct hists *hists_base,
653
struct hists *hists_pair)
654
{
655
struct rb_root_cached *root = hists_base->entries_in;
656
struct rb_node *next = rb_first_cached(root);
657
658
while (next != NULL) {
659
struct hist_entry *he = rb_entry(next, struct hist_entry,
660
rb_node_in);
661
struct hist_entry *pair = get_block_pair(he, hists_pair);
662
663
next = rb_next(&he->rb_node_in);
664
665
if (pair) {
666
hist_entry__add_pair(pair, he);
667
compute_cycles_diff(he, pair);
668
}
669
}
670
}
671
672
static void hists__precompute(struct hists *hists)
673
{
674
struct rb_root_cached *root;
675
struct rb_node *next;
676
677
if (hists__has(hists, need_collapse))
678
root = &hists->entries_collapsed;
679
else
680
root = hists->entries_in;
681
682
next = rb_first_cached(root);
683
while (next != NULL) {
684
struct block_hist *bh, *pair_bh;
685
struct hist_entry *he, *pair;
686
struct data__file *d;
687
int i;
688
689
he = rb_entry(next, struct hist_entry, rb_node_in);
690
next = rb_next(&he->rb_node_in);
691
692
if (compute == COMPUTE_CYCLES) {
693
bh = container_of(he, struct block_hist, he);
694
init_block_hist(bh);
695
block_info__process_sym(he, bh, NULL, 0, 0);
696
}
697
698
data__for_each_file_new(i, d) {
699
pair = get_pair_data(he, d);
700
if (!pair)
701
continue;
702
703
switch (compute) {
704
case COMPUTE_DELTA:
705
case COMPUTE_DELTA_ABS:
706
compute_delta(he, pair);
707
break;
708
case COMPUTE_RATIO:
709
compute_ratio(he, pair);
710
break;
711
case COMPUTE_WEIGHTED_DIFF:
712
compute_wdiff(he, pair);
713
break;
714
case COMPUTE_CYCLES:
715
pair_bh = container_of(pair, struct block_hist,
716
he);
717
init_block_hist(pair_bh);
718
block_info__process_sym(pair, pair_bh, NULL, 0, 0);
719
720
bh = container_of(he, struct block_hist, he);
721
722
if (bh->valid && pair_bh->valid) {
723
block_hists_match(&bh->block_hists,
724
&pair_bh->block_hists);
725
hists__output_resort(&pair_bh->block_hists,
726
NULL);
727
}
728
break;
729
default:
730
BUG_ON(1);
731
}
732
}
733
}
734
}
735
736
static int64_t cmp_doubles(double l, double r)
737
{
738
if (l > r)
739
return -1;
740
else if (l < r)
741
return 1;
742
else
743
return 0;
744
}
745
746
static int64_t
747
__hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
748
int c)
749
{
750
switch (c) {
751
case COMPUTE_DELTA:
752
{
753
double l = left->diff.period_ratio_delta;
754
double r = right->diff.period_ratio_delta;
755
756
return cmp_doubles(l, r);
757
}
758
case COMPUTE_DELTA_ABS:
759
{
760
double l = fabs(left->diff.period_ratio_delta);
761
double r = fabs(right->diff.period_ratio_delta);
762
763
return cmp_doubles(l, r);
764
}
765
case COMPUTE_RATIO:
766
{
767
double l = left->diff.period_ratio;
768
double r = right->diff.period_ratio;
769
770
return cmp_doubles(l, r);
771
}
772
case COMPUTE_WEIGHTED_DIFF:
773
{
774
s64 l = left->diff.wdiff;
775
s64 r = right->diff.wdiff;
776
777
return r - l;
778
}
779
default:
780
BUG_ON(1);
781
}
782
783
return 0;
784
}
785
786
static int64_t
787
hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
788
int c, int sort_idx)
789
{
790
bool pairs_left = hist_entry__has_pairs(left);
791
bool pairs_right = hist_entry__has_pairs(right);
792
struct hist_entry *p_right, *p_left;
793
794
if (!pairs_left && !pairs_right)
795
return 0;
796
797
if (!pairs_left || !pairs_right)
798
return pairs_left ? -1 : 1;
799
800
p_left = get_pair_data(left, &data__files[sort_idx]);
801
p_right = get_pair_data(right, &data__files[sort_idx]);
802
803
if (!p_left && !p_right)
804
return 0;
805
806
if (!p_left || !p_right)
807
return p_left ? -1 : 1;
808
809
/*
810
* We have 2 entries of same kind, let's
811
* make the data comparison.
812
*/
813
return __hist_entry__cmp_compute(p_left, p_right, c);
814
}
815
816
static int64_t
817
hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right,
818
int c, int sort_idx)
819
{
820
struct hist_entry *p_right, *p_left;
821
822
p_left = get_pair_data(left, &data__files[sort_idx]);
823
p_right = get_pair_data(right, &data__files[sort_idx]);
824
825
if (!p_left && !p_right)
826
return 0;
827
828
if (!p_left || !p_right)
829
return p_left ? -1 : 1;
830
831
if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) {
832
/*
833
* The delta can be computed without the baseline, but
834
* others are not. Put those entries which have no
835
* values below.
836
*/
837
if (left->dummy && right->dummy)
838
return 0;
839
840
if (left->dummy || right->dummy)
841
return left->dummy ? 1 : -1;
842
}
843
844
return __hist_entry__cmp_compute(p_left, p_right, c);
845
}
846
847
static int64_t
848
hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused,
849
struct hist_entry *left __maybe_unused,
850
struct hist_entry *right __maybe_unused)
851
{
852
return 0;
853
}
854
855
static int64_t
856
hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused,
857
struct hist_entry *left, struct hist_entry *right)
858
{
859
if (left->stat.period == right->stat.period)
860
return 0;
861
return left->stat.period > right->stat.period ? 1 : -1;
862
}
863
864
static int64_t
865
hist_entry__cmp_delta(struct perf_hpp_fmt *fmt,
866
struct hist_entry *left, struct hist_entry *right)
867
{
868
struct data__file *d = fmt_to_data_file(fmt);
869
870
return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx);
871
}
872
873
static int64_t
874
hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt,
875
struct hist_entry *left, struct hist_entry *right)
876
{
877
struct data__file *d = fmt_to_data_file(fmt);
878
879
return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx);
880
}
881
882
static int64_t
883
hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt,
884
struct hist_entry *left, struct hist_entry *right)
885
{
886
struct data__file *d = fmt_to_data_file(fmt);
887
888
return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx);
889
}
890
891
static int64_t
892
hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt,
893
struct hist_entry *left, struct hist_entry *right)
894
{
895
struct data__file *d = fmt_to_data_file(fmt);
896
897
return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx);
898
}
899
900
static int64_t
901
hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused,
902
struct hist_entry *left, struct hist_entry *right)
903
{
904
return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA,
905
sort_compute);
906
}
907
908
static int64_t
909
hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused,
910
struct hist_entry *left, struct hist_entry *right)
911
{
912
return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS,
913
sort_compute);
914
}
915
916
static int64_t
917
hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused,
918
struct hist_entry *left, struct hist_entry *right)
919
{
920
return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO,
921
sort_compute);
922
}
923
924
static int64_t
925
hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused,
926
struct hist_entry *left, struct hist_entry *right)
927
{
928
return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF,
929
sort_compute);
930
}
931
932
static void hists__process(struct hists *hists)
933
{
934
if (show_baseline_only)
935
hists__baseline_only(hists);
936
937
hists__precompute(hists);
938
hists__output_resort(hists, NULL);
939
940
if (compute == COMPUTE_CYCLES)
941
symbol_conf.report_block = true;
942
943
hists__fprintf(hists, !quiet, 0, 0, 0, stdout,
944
!symbol_conf.use_callchain);
945
}
946
947
static void data__fprintf(void)
948
{
949
struct data__file *d;
950
int i;
951
952
fprintf(stdout, "# Data files:\n");
953
954
data__for_each_file(i, d)
955
fprintf(stdout, "# [%d] %s %s\n",
956
d->idx, d->data.path,
957
!d->idx ? "(Baseline)" : "");
958
959
fprintf(stdout, "#\n");
960
}
961
962
static void data_process(void)
963
{
964
struct evlist *evlist_base = data__files[0].session->evlist;
965
struct evsel *evsel_base;
966
bool first = true;
967
968
evlist__for_each_entry(evlist_base, evsel_base) {
969
struct hists *hists_base = evsel__hists(evsel_base);
970
struct data__file *d;
971
int i;
972
973
data__for_each_file_new(i, d) {
974
struct evlist *evlist = d->session->evlist;
975
struct evsel *evsel;
976
struct hists *hists;
977
978
evsel = evsel_match(evsel_base, evlist);
979
if (!evsel)
980
continue;
981
982
hists = evsel__hists(evsel);
983
d->hists = hists;
984
985
hists__match(hists_base, hists);
986
987
if (!show_baseline_only)
988
hists__link(hists_base, hists);
989
}
990
991
if (!quiet) {
992
fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
993
evsel__name(evsel_base));
994
}
995
996
first = false;
997
998
if (verbose > 0 || ((data__files_cnt > 2) && !quiet))
999
data__fprintf();
1000
1001
/* Don't sort callchain for perf diff */
1002
evsel__reset_sample_bit(evsel_base, CALLCHAIN);
1003
1004
hists__process(hists_base);
1005
}
1006
}
1007
1008
static int process_base_stream(struct data__file *data_base,
1009
struct data__file *data_pair,
1010
const char *title __maybe_unused)
1011
{
1012
struct evlist *evlist_base = data_base->session->evlist;
1013
struct evlist *evlist_pair = data_pair->session->evlist;
1014
struct evsel *evsel_base, *evsel_pair;
1015
struct evsel_streams *es_base, *es_pair;
1016
1017
evlist__for_each_entry(evlist_base, evsel_base) {
1018
evsel_pair = evsel_match(evsel_base, evlist_pair);
1019
if (!evsel_pair)
1020
continue;
1021
1022
es_base = evsel_streams__entry(data_base->evlist_streams,
1023
evsel_base);
1024
if (!es_base)
1025
return -1;
1026
1027
es_pair = evsel_streams__entry(data_pair->evlist_streams,
1028
evsel_pair);
1029
if (!es_pair)
1030
return -1;
1031
1032
evsel_streams__match(es_base, es_pair);
1033
evsel_streams__report(es_base, es_pair);
1034
}
1035
1036
return 0;
1037
}
1038
1039
static void stream_process(void)
1040
{
1041
/*
1042
* Stream comparison only supports two data files.
1043
* perf.data.old and perf.data. data__files[0] is perf.data.old,
1044
* data__files[1] is perf.data.
1045
*/
1046
process_base_stream(&data__files[0], &data__files[1],
1047
"# Output based on old perf data:\n#\n");
1048
}
1049
1050
static void data__free(struct data__file *d)
1051
{
1052
int col;
1053
1054
if (d->evlist_streams)
1055
evlist_streams__delete(d->evlist_streams);
1056
1057
for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
1058
struct diff_hpp_fmt *fmt = &d->fmt[col];
1059
1060
zfree(&fmt->header);
1061
}
1062
}
1063
1064
static int abstime_str_dup(char **pstr)
1065
{
1066
char *str = NULL;
1067
1068
if (pdiff.time_str && strchr(pdiff.time_str, ':')) {
1069
str = strdup(pdiff.time_str);
1070
if (!str)
1071
return -ENOMEM;
1072
}
1073
1074
*pstr = str;
1075
return 0;
1076
}
1077
1078
static int parse_absolute_time(struct data__file *d, char **pstr)
1079
{
1080
char *p = *pstr;
1081
int ret;
1082
1083
/*
1084
* Absolute timestamp for one file has the format: a.b,c.d
1085
* For multiple files, the format is: a.b,c.d:a.b,c.d
1086
*/
1087
p = strchr(*pstr, ':');
1088
if (p) {
1089
if (p == *pstr) {
1090
pr_err("Invalid time string\n");
1091
return -EINVAL;
1092
}
1093
1094
*p = 0;
1095
p++;
1096
if (*p == 0) {
1097
pr_err("Invalid time string\n");
1098
return -EINVAL;
1099
}
1100
}
1101
1102
ret = perf_time__parse_for_ranges(*pstr, d->session,
1103
&pdiff.ptime_range,
1104
&pdiff.range_size,
1105
&pdiff.range_num);
1106
if (ret < 0)
1107
return ret;
1108
1109
if (!p || *p == 0)
1110
*pstr = NULL;
1111
else
1112
*pstr = p;
1113
1114
return ret;
1115
}
1116
1117
static int parse_percent_time(struct data__file *d)
1118
{
1119
int ret;
1120
1121
ret = perf_time__parse_for_ranges(pdiff.time_str, d->session,
1122
&pdiff.ptime_range,
1123
&pdiff.range_size,
1124
&pdiff.range_num);
1125
return ret;
1126
}
1127
1128
static int parse_time_str(struct data__file *d, char *abstime_ostr,
1129
char **pabstime_tmp)
1130
{
1131
int ret = 0;
1132
1133
if (abstime_ostr)
1134
ret = parse_absolute_time(d, pabstime_tmp);
1135
else if (pdiff.time_str)
1136
ret = parse_percent_time(d);
1137
1138
return ret;
1139
}
1140
1141
static int check_file_brstack(void)
1142
{
1143
struct data__file *d;
1144
bool has_br_stack;
1145
int i;
1146
1147
data__for_each_file(i, d) {
1148
d->session = perf_session__new(&d->data, &pdiff.tool);
1149
if (IS_ERR(d->session)) {
1150
pr_err("Failed to open %s\n", d->data.path);
1151
return PTR_ERR(d->session);
1152
}
1153
1154
has_br_stack = perf_header__has_feat(&d->session->header,
1155
HEADER_BRANCH_STACK);
1156
perf_session__delete(d->session);
1157
if (!has_br_stack)
1158
return 0;
1159
}
1160
1161
/* Set only all files having branch stacks */
1162
pdiff.has_br_stack = true;
1163
return 0;
1164
}
1165
1166
static int __cmd_diff(void)
1167
{
1168
struct data__file *d;
1169
int ret, i;
1170
char *abstime_ostr, *abstime_tmp;
1171
1172
ret = abstime_str_dup(&abstime_ostr);
1173
if (ret)
1174
return ret;
1175
1176
abstime_tmp = abstime_ostr;
1177
ret = -EINVAL;
1178
1179
data__for_each_file(i, d) {
1180
d->session = perf_session__new(&d->data, &pdiff.tool);
1181
if (IS_ERR(d->session)) {
1182
ret = PTR_ERR(d->session);
1183
pr_err("Failed to open %s\n", d->data.path);
1184
goto out_delete;
1185
}
1186
1187
if (pdiff.time_str) {
1188
ret = parse_time_str(d, abstime_ostr, &abstime_tmp);
1189
if (ret < 0)
1190
goto out_delete;
1191
}
1192
1193
if (cpu_list) {
1194
ret = perf_session__cpu_bitmap(d->session, cpu_list,
1195
cpu_bitmap);
1196
if (ret < 0)
1197
goto out_delete;
1198
}
1199
1200
ret = perf_session__process_events(d->session);
1201
if (ret) {
1202
pr_err("Failed to process %s\n", d->data.path);
1203
goto out_delete;
1204
}
1205
1206
evlist__collapse_resort(d->session->evlist);
1207
1208
if (pdiff.ptime_range)
1209
zfree(&pdiff.ptime_range);
1210
1211
if (compute == COMPUTE_STREAM) {
1212
d->evlist_streams = evlist__create_streams(
1213
d->session->evlist, 5);
1214
if (!d->evlist_streams) {
1215
ret = -ENOMEM;
1216
goto out_delete;
1217
}
1218
}
1219
}
1220
1221
if (compute == COMPUTE_STREAM)
1222
stream_process();
1223
else
1224
data_process();
1225
1226
out_delete:
1227
data__for_each_file(i, d) {
1228
if (!IS_ERR(d->session))
1229
perf_session__delete(d->session);
1230
data__free(d);
1231
}
1232
1233
free(data__files);
1234
1235
if (pdiff.ptime_range)
1236
zfree(&pdiff.ptime_range);
1237
1238
if (abstime_ostr)
1239
free(abstime_ostr);
1240
1241
return ret;
1242
}
1243
1244
static const char * const diff_usage[] = {
1245
"perf diff [<options>] [old_file] [new_file]",
1246
NULL,
1247
};
1248
1249
static const struct option options[] = {
1250
OPT_INCR('v', "verbose", &verbose,
1251
"be more verbose (show symbol address, etc)"),
1252
OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any warnings or messages"),
1253
OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
1254
"Show only items with match in baseline"),
1255
OPT_CALLBACK('c', "compute", &compute,
1256
"delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs),cycles",
1257
"Entries differential computation selection",
1258
setup_compute),
1259
OPT_BOOLEAN('p', "period", &show_period,
1260
"Show period values."),
1261
OPT_BOOLEAN('F', "formula", &show_formula,
1262
"Show formula."),
1263
OPT_BOOLEAN(0, "cycles-hist", &cycles_hist,
1264
"Show cycles histogram and standard deviation "
1265
"- WARNING: use only with -c cycles."),
1266
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1267
"dump raw trace in ASCII"),
1268
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
1269
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
1270
"file", "kallsyms pathname"),
1271
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
1272
"load module symbols - WARNING: use only with -k and LIVE kernel"),
1273
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
1274
"only consider symbols in these dsos"),
1275
OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
1276
"only consider symbols in these comms"),
1277
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
1278
"only consider these symbols"),
1279
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
1280
"sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
1281
" Please refer the man page for the complete list."),
1282
OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
1283
"separator for columns, no spaces will be added between "
1284
"columns '.' is reserved."),
1285
OPT_CALLBACK(0, "symfs", NULL, "directory",
1286
"Look for files with symbols relative to this directory",
1287
symbol__config_symfs),
1288
OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
1289
OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
1290
"How to display percentage of filtered entries", parse_filter_percentage),
1291
OPT_STRING(0, "time", &pdiff.time_str, "str",
1292
"Time span (time percent or absolute timestamp)"),
1293
OPT_STRING(0, "cpu", &cpu_list, "cpu", "list of cpus to profile"),
1294
OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
1295
"only consider symbols in these pids"),
1296
OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
1297
"only consider symbols in these tids"),
1298
OPT_BOOLEAN(0, "stream", &pdiff.stream,
1299
"Enable hot streams comparison."),
1300
OPT_END()
1301
};
1302
1303
static double baseline_percent(struct hist_entry *he)
1304
{
1305
u64 total = hists__total_period(he->hists);
1306
1307
return 100.0 * he->stat.period / total;
1308
}
1309
1310
static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
1311
struct perf_hpp *hpp, struct hist_entry *he)
1312
{
1313
struct diff_hpp_fmt *dfmt =
1314
container_of(fmt, struct diff_hpp_fmt, fmt);
1315
double percent = baseline_percent(he);
1316
char pfmt[20] = " ";
1317
1318
if (!he->dummy) {
1319
scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1);
1320
return percent_color_snprintf(hpp->buf, hpp->size,
1321
pfmt, percent);
1322
} else
1323
return scnprintf(hpp->buf, hpp->size, "%*s",
1324
dfmt->header_width, pfmt);
1325
}
1326
1327
static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
1328
{
1329
double percent = baseline_percent(he);
1330
const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
1331
int ret = 0;
1332
1333
if (!he->dummy)
1334
ret = scnprintf(buf, size, fmt, percent);
1335
1336
return ret;
1337
}
1338
1339
static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
1340
struct perf_hpp *hpp, int width)
1341
{
1342
struct block_hist *bh = container_of(he, struct block_hist, he);
1343
struct block_hist *bh_pair = container_of(pair, struct block_hist, he);
1344
struct hist_entry *block_he;
1345
struct block_info *bi;
1346
char buf[128];
1347
char *start_line, *end_line;
1348
1349
block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx);
1350
if (!block_he) {
1351
hpp->skip = true;
1352
return 0;
1353
}
1354
1355
/*
1356
* Avoid printing the warning "addr2line_init failed for ..."
1357
*/
1358
symbol_conf.disable_add2line_warn = true;
1359
1360
bi = block_he->block_info;
1361
1362
start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
1363
he->ms.sym);
1364
1365
end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
1366
he->ms.sym);
1367
1368
if (start_line != SRCLINE_UNKNOWN &&
1369
end_line != SRCLINE_UNKNOWN) {
1370
scnprintf(buf, sizeof(buf), "[%s -> %s] %4ld",
1371
start_line, end_line, block_he->diff.cycles);
1372
} else {
1373
scnprintf(buf, sizeof(buf), "[%7lx -> %7lx] %4ld",
1374
bi->start, bi->end, block_he->diff.cycles);
1375
}
1376
1377
zfree_srcline(&start_line);
1378
zfree_srcline(&end_line);
1379
1380
return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1381
}
1382
1383
static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
1384
struct perf_hpp *hpp, struct hist_entry *he,
1385
int comparison_method)
1386
{
1387
struct diff_hpp_fmt *dfmt =
1388
container_of(fmt, struct diff_hpp_fmt, fmt);
1389
struct hist_entry *pair = get_pair_fmt(he, dfmt);
1390
double diff;
1391
s64 wdiff;
1392
char pfmt[20] = " ";
1393
1394
if (!pair) {
1395
if (comparison_method == COMPUTE_CYCLES) {
1396
struct block_hist *bh;
1397
1398
bh = container_of(he, struct block_hist, he);
1399
if (bh->block_idx)
1400
hpp->skip = true;
1401
}
1402
1403
goto no_print;
1404
}
1405
1406
switch (comparison_method) {
1407
case COMPUTE_DELTA:
1408
if (pair->diff.computed)
1409
diff = pair->diff.period_ratio_delta;
1410
else
1411
diff = compute_delta(he, pair);
1412
1413
scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1);
1414
return percent_color_snprintf(hpp->buf, hpp->size,
1415
pfmt, diff);
1416
case COMPUTE_RATIO:
1417
if (he->dummy)
1418
goto dummy_print;
1419
if (pair->diff.computed)
1420
diff = pair->diff.period_ratio;
1421
else
1422
diff = compute_ratio(he, pair);
1423
1424
scnprintf(pfmt, 20, "%%%d.6f", dfmt->header_width);
1425
return value_color_snprintf(hpp->buf, hpp->size,
1426
pfmt, diff);
1427
case COMPUTE_WEIGHTED_DIFF:
1428
if (he->dummy)
1429
goto dummy_print;
1430
if (pair->diff.computed)
1431
wdiff = pair->diff.wdiff;
1432
else
1433
wdiff = compute_wdiff(he, pair);
1434
1435
scnprintf(pfmt, 20, "%%14ld", dfmt->header_width);
1436
return color_snprintf(hpp->buf, hpp->size,
1437
get_percent_color(wdiff),
1438
pfmt, wdiff);
1439
case COMPUTE_CYCLES:
1440
return cycles_printf(he, pair, hpp, dfmt->header_width);
1441
default:
1442
BUG_ON(1);
1443
}
1444
dummy_print:
1445
return scnprintf(hpp->buf, hpp->size, "%*s",
1446
dfmt->header_width, "N/A");
1447
no_print:
1448
return scnprintf(hpp->buf, hpp->size, "%*s",
1449
dfmt->header_width, pfmt);
1450
}
1451
1452
static int hpp__color_delta(struct perf_hpp_fmt *fmt,
1453
struct perf_hpp *hpp, struct hist_entry *he)
1454
{
1455
return __hpp__color_compare(fmt, hpp, he, COMPUTE_DELTA);
1456
}
1457
1458
static int hpp__color_ratio(struct perf_hpp_fmt *fmt,
1459
struct perf_hpp *hpp, struct hist_entry *he)
1460
{
1461
return __hpp__color_compare(fmt, hpp, he, COMPUTE_RATIO);
1462
}
1463
1464
static int hpp__color_wdiff(struct perf_hpp_fmt *fmt,
1465
struct perf_hpp *hpp, struct hist_entry *he)
1466
{
1467
return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
1468
}
1469
1470
static int hpp__color_cycles(struct perf_hpp_fmt *fmt,
1471
struct perf_hpp *hpp, struct hist_entry *he)
1472
{
1473
return __hpp__color_compare(fmt, hpp, he, COMPUTE_CYCLES);
1474
}
1475
1476
static int all_zero(unsigned long *vals, int len)
1477
{
1478
int i;
1479
1480
for (i = 0; i < len; i++)
1481
if (vals[i] != 0)
1482
return 0;
1483
return 1;
1484
}
1485
1486
static int print_cycles_spark(char *bf, int size, unsigned long *svals, u64 n)
1487
{
1488
int printed;
1489
1490
if (n <= 1)
1491
return 0;
1492
1493
if (n > NUM_SPARKS)
1494
n = NUM_SPARKS;
1495
if (all_zero(svals, n))
1496
return 0;
1497
1498
printed = print_spark(bf, size, svals, n);
1499
printed += scnprintf(bf + printed, size - printed, " ");
1500
return printed;
1501
}
1502
1503
static int hpp__color_cycles_hist(struct perf_hpp_fmt *fmt,
1504
struct perf_hpp *hpp, struct hist_entry *he)
1505
{
1506
struct diff_hpp_fmt *dfmt =
1507
container_of(fmt, struct diff_hpp_fmt, fmt);
1508
struct hist_entry *pair = get_pair_fmt(he, dfmt);
1509
struct block_hist *bh = container_of(he, struct block_hist, he);
1510
struct block_hist *bh_pair;
1511
struct hist_entry *block_he;
1512
char spark[32], buf[128];
1513
double r;
1514
int ret, pad;
1515
1516
if (!pair) {
1517
if (bh->block_idx)
1518
hpp->skip = true;
1519
1520
goto no_print;
1521
}
1522
1523
bh_pair = container_of(pair, struct block_hist, he);
1524
1525
block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx);
1526
if (!block_he) {
1527
hpp->skip = true;
1528
goto no_print;
1529
}
1530
1531
ret = print_cycles_spark(spark, sizeof(spark), block_he->diff.svals,
1532
block_he->diff.stats.n);
1533
1534
r = rel_stddev_stats(stddev_stats(&block_he->diff.stats),
1535
avg_stats(&block_he->diff.stats));
1536
1537
if (ret) {
1538
/*
1539
* Padding spaces if number of sparks less than NUM_SPARKS
1540
* otherwise the output is not aligned.
1541
*/
1542
pad = NUM_SPARKS - ((ret - 1) / 3);
1543
scnprintf(buf, sizeof(buf), "%s%5.1f%% %s", "\u00B1", r, spark);
1544
ret = scnprintf(hpp->buf, hpp->size, "%*s",
1545
dfmt->header_width, buf);
1546
1547
if (pad) {
1548
ret += scnprintf(hpp->buf + ret, hpp->size - ret,
1549
"%-*s", pad, " ");
1550
}
1551
1552
return ret;
1553
}
1554
1555
no_print:
1556
return scnprintf(hpp->buf, hpp->size, "%*s",
1557
dfmt->header_width, " ");
1558
}
1559
1560
static void
1561
hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
1562
{
1563
switch (idx) {
1564
case PERF_HPP_DIFF__PERIOD_BASELINE:
1565
scnprintf(buf, size, "%" PRIu64, he->stat.period);
1566
break;
1567
1568
default:
1569
break;
1570
}
1571
}
1572
1573
static void
1574
hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
1575
int idx, char *buf, size_t size)
1576
{
1577
double diff;
1578
double ratio;
1579
s64 wdiff;
1580
1581
switch (idx) {
1582
case PERF_HPP_DIFF__DELTA:
1583
case PERF_HPP_DIFF__DELTA_ABS:
1584
if (pair->diff.computed)
1585
diff = pair->diff.period_ratio_delta;
1586
else
1587
diff = compute_delta(he, pair);
1588
1589
scnprintf(buf, size, "%+4.2F%%", diff);
1590
break;
1591
1592
case PERF_HPP_DIFF__RATIO:
1593
/* No point for ratio number if we are dummy.. */
1594
if (he->dummy) {
1595
scnprintf(buf, size, "N/A");
1596
break;
1597
}
1598
1599
if (pair->diff.computed)
1600
ratio = pair->diff.period_ratio;
1601
else
1602
ratio = compute_ratio(he, pair);
1603
1604
if (ratio > 0.0)
1605
scnprintf(buf, size, "%14.6F", ratio);
1606
break;
1607
1608
case PERF_HPP_DIFF__WEIGHTED_DIFF:
1609
/* No point for wdiff number if we are dummy.. */
1610
if (he->dummy) {
1611
scnprintf(buf, size, "N/A");
1612
break;
1613
}
1614
1615
if (pair->diff.computed)
1616
wdiff = pair->diff.wdiff;
1617
else
1618
wdiff = compute_wdiff(he, pair);
1619
1620
if (wdiff != 0)
1621
scnprintf(buf, size, "%14ld", wdiff);
1622
break;
1623
1624
case PERF_HPP_DIFF__FORMULA:
1625
formula_fprintf(he, pair, buf, size);
1626
break;
1627
1628
case PERF_HPP_DIFF__PERIOD:
1629
scnprintf(buf, size, "%" PRIu64, pair->stat.period);
1630
break;
1631
1632
default:
1633
BUG_ON(1);
1634
}
1635
}
1636
1637
static void
1638
__hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt,
1639
char *buf, size_t size)
1640
{
1641
struct hist_entry *pair = get_pair_fmt(he, dfmt);
1642
int idx = dfmt->idx;
1643
1644
/* baseline is special */
1645
if (idx == PERF_HPP_DIFF__BASELINE)
1646
hpp__entry_baseline(he, buf, size);
1647
else {
1648
if (pair)
1649
hpp__entry_pair(he, pair, idx, buf, size);
1650
else
1651
hpp__entry_unpair(he, idx, buf, size);
1652
}
1653
}
1654
1655
static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,
1656
struct hist_entry *he)
1657
{
1658
struct diff_hpp_fmt *dfmt =
1659
container_of(_fmt, struct diff_hpp_fmt, fmt);
1660
char buf[MAX_COL_WIDTH] = " ";
1661
1662
__hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH);
1663
1664
if (symbol_conf.field_sep)
1665
return scnprintf(hpp->buf, hpp->size, "%s", buf);
1666
else
1667
return scnprintf(hpp->buf, hpp->size, "%*s",
1668
dfmt->header_width, buf);
1669
}
1670
1671
static int hpp__header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1672
struct hists *hists __maybe_unused,
1673
int line __maybe_unused,
1674
int *span __maybe_unused)
1675
{
1676
struct diff_hpp_fmt *dfmt =
1677
container_of(fmt, struct diff_hpp_fmt, fmt);
1678
1679
BUG_ON(!dfmt->header);
1680
return scnprintf(hpp->buf, hpp->size, dfmt->header);
1681
}
1682
1683
static int hpp__width(struct perf_hpp_fmt *fmt,
1684
struct perf_hpp *hpp __maybe_unused,
1685
struct hists *hists __maybe_unused)
1686
{
1687
struct diff_hpp_fmt *dfmt =
1688
container_of(fmt, struct diff_hpp_fmt, fmt);
1689
1690
BUG_ON(dfmt->header_width <= 0);
1691
return dfmt->header_width;
1692
}
1693
1694
static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt)
1695
{
1696
#define MAX_HEADER_NAME 100
1697
char buf_indent[MAX_HEADER_NAME];
1698
char buf[MAX_HEADER_NAME];
1699
const char *header = NULL;
1700
int width = 0;
1701
1702
BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX);
1703
header = columns[dfmt->idx].name;
1704
width = columns[dfmt->idx].width;
1705
1706
/* Only our defined HPP fmts should appear here. */
1707
BUG_ON(!header);
1708
1709
if (data__files_cnt > 2)
1710
scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx);
1711
1712
#define NAME (data__files_cnt > 2 ? buf : header)
1713
dfmt->header_width = width;
1714
width = (int) strlen(NAME);
1715
if (dfmt->header_width < width)
1716
dfmt->header_width = width;
1717
1718
scnprintf(buf_indent, MAX_HEADER_NAME, "%*s",
1719
dfmt->header_width, NAME);
1720
1721
dfmt->header = strdup(buf_indent);
1722
#undef MAX_HEADER_NAME
1723
#undef NAME
1724
}
1725
1726
static void data__hpp_register(struct data__file *d, int idx)
1727
{
1728
struct diff_hpp_fmt *dfmt = &d->fmt[idx];
1729
struct perf_hpp_fmt *fmt = &dfmt->fmt;
1730
1731
dfmt->idx = idx;
1732
1733
fmt->header = hpp__header;
1734
fmt->width = hpp__width;
1735
fmt->entry = hpp__entry_global;
1736
fmt->cmp = hist_entry__cmp_nop;
1737
fmt->collapse = hist_entry__cmp_nop;
1738
1739
/* TODO more colors */
1740
switch (idx) {
1741
case PERF_HPP_DIFF__BASELINE:
1742
fmt->color = hpp__color_baseline;
1743
fmt->sort = hist_entry__cmp_baseline;
1744
break;
1745
case PERF_HPP_DIFF__DELTA:
1746
fmt->color = hpp__color_delta;
1747
fmt->sort = hist_entry__cmp_delta;
1748
break;
1749
case PERF_HPP_DIFF__RATIO:
1750
fmt->color = hpp__color_ratio;
1751
fmt->sort = hist_entry__cmp_ratio;
1752
break;
1753
case PERF_HPP_DIFF__WEIGHTED_DIFF:
1754
fmt->color = hpp__color_wdiff;
1755
fmt->sort = hist_entry__cmp_wdiff;
1756
break;
1757
case PERF_HPP_DIFF__DELTA_ABS:
1758
fmt->color = hpp__color_delta;
1759
fmt->sort = hist_entry__cmp_delta_abs;
1760
break;
1761
case PERF_HPP_DIFF__CYCLES:
1762
fmt->color = hpp__color_cycles;
1763
fmt->sort = hist_entry__cmp_nop;
1764
break;
1765
case PERF_HPP_DIFF__CYCLES_HIST:
1766
fmt->color = hpp__color_cycles_hist;
1767
fmt->sort = hist_entry__cmp_nop;
1768
break;
1769
default:
1770
fmt->sort = hist_entry__cmp_nop;
1771
break;
1772
}
1773
1774
init_header(d, dfmt);
1775
perf_hpp__column_register(fmt);
1776
perf_hpp__register_sort_field(fmt);
1777
}
1778
1779
static int ui_init(void)
1780
{
1781
struct data__file *d;
1782
struct perf_hpp_fmt *fmt;
1783
int i;
1784
1785
data__for_each_file(i, d) {
1786
1787
/*
1788
* Baseline or compute related columns:
1789
*
1790
* PERF_HPP_DIFF__BASELINE
1791
* PERF_HPP_DIFF__DELTA
1792
* PERF_HPP_DIFF__RATIO
1793
* PERF_HPP_DIFF__WEIGHTED_DIFF
1794
* PERF_HPP_DIFF__CYCLES
1795
*/
1796
data__hpp_register(d, i ? compute_2_hpp[compute] :
1797
PERF_HPP_DIFF__BASELINE);
1798
1799
if (cycles_hist && i)
1800
data__hpp_register(d, PERF_HPP_DIFF__CYCLES_HIST);
1801
1802
/*
1803
* And the rest:
1804
*
1805
* PERF_HPP_DIFF__FORMULA
1806
* PERF_HPP_DIFF__PERIOD
1807
* PERF_HPP_DIFF__PERIOD_BASELINE
1808
*/
1809
if (show_formula && i)
1810
data__hpp_register(d, PERF_HPP_DIFF__FORMULA);
1811
1812
if (show_period)
1813
data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
1814
PERF_HPP_DIFF__PERIOD_BASELINE);
1815
}
1816
1817
if (!sort_compute)
1818
return 0;
1819
1820
/*
1821
* Prepend an fmt to sort on columns at 'sort_compute' first.
1822
* This fmt is added only to the sort list but not to the
1823
* output fields list.
1824
*
1825
* Note that this column (data) can be compared twice - one
1826
* for this 'sort_compute' fmt and another for the normal
1827
* diff_hpp_fmt. But it shouldn't a problem as most entries
1828
* will be sorted out by first try or baseline and comparing
1829
* is not a costly operation.
1830
*/
1831
fmt = zalloc(sizeof(*fmt));
1832
if (fmt == NULL) {
1833
pr_err("Memory allocation failed\n");
1834
return -1;
1835
}
1836
1837
fmt->cmp = hist_entry__cmp_nop;
1838
fmt->collapse = hist_entry__cmp_nop;
1839
1840
switch (compute) {
1841
case COMPUTE_DELTA:
1842
fmt->sort = hist_entry__cmp_delta_idx;
1843
break;
1844
case COMPUTE_RATIO:
1845
fmt->sort = hist_entry__cmp_ratio_idx;
1846
break;
1847
case COMPUTE_WEIGHTED_DIFF:
1848
fmt->sort = hist_entry__cmp_wdiff_idx;
1849
break;
1850
case COMPUTE_DELTA_ABS:
1851
fmt->sort = hist_entry__cmp_delta_abs_idx;
1852
break;
1853
case COMPUTE_CYCLES:
1854
/*
1855
* Should set since 'fmt->sort' is called without
1856
* checking valid during sorting
1857
*/
1858
fmt->sort = hist_entry__cmp_nop;
1859
break;
1860
default:
1861
BUG_ON(1);
1862
}
1863
1864
perf_hpp__prepend_sort_field(fmt);
1865
return 0;
1866
}
1867
1868
static int data_init(int argc, const char **argv)
1869
{
1870
struct data__file *d;
1871
static const char *defaults[] = {
1872
"perf.data.old",
1873
"perf.data",
1874
};
1875
bool use_default = true;
1876
int i;
1877
1878
data__files_cnt = 2;
1879
1880
if (argc) {
1881
if (argc == 1)
1882
defaults[1] = argv[0];
1883
else {
1884
data__files_cnt = argc;
1885
use_default = false;
1886
}
1887
} else if (perf_guest) {
1888
defaults[0] = "perf.data.host";
1889
defaults[1] = "perf.data.guest";
1890
}
1891
1892
if (sort_compute >= (unsigned int) data__files_cnt) {
1893
pr_err("Order option out of limit.\n");
1894
return -EINVAL;
1895
}
1896
1897
data__files = zalloc(sizeof(*data__files) * data__files_cnt);
1898
if (!data__files)
1899
return -ENOMEM;
1900
1901
data__for_each_file(i, d) {
1902
struct perf_data *data = &d->data;
1903
1904
data->path = use_default ? defaults[i] : argv[i];
1905
data->mode = PERF_DATA_MODE_READ;
1906
data->force = force;
1907
1908
d->idx = i;
1909
}
1910
1911
return 0;
1912
}
1913
1914
static int diff__config(const char *var, const char *value,
1915
void *cb __maybe_unused)
1916
{
1917
if (!strcmp(var, "diff.order")) {
1918
int ret;
1919
if (perf_config_int(&ret, var, value) < 0)
1920
return -1;
1921
sort_compute = ret;
1922
return 0;
1923
}
1924
if (!strcmp(var, "diff.compute")) {
1925
if (!strcmp(value, "delta")) {
1926
compute = COMPUTE_DELTA;
1927
} else if (!strcmp(value, "delta-abs")) {
1928
compute = COMPUTE_DELTA_ABS;
1929
} else if (!strcmp(value, "ratio")) {
1930
compute = COMPUTE_RATIO;
1931
} else if (!strcmp(value, "wdiff")) {
1932
compute = COMPUTE_WEIGHTED_DIFF;
1933
} else {
1934
pr_err("Invalid compute method: %s\n", value);
1935
return -1;
1936
}
1937
}
1938
1939
return 0;
1940
}
1941
1942
int cmd_diff(int argc, const char **argv)
1943
{
1944
int ret = hists__init();
1945
1946
if (ret < 0)
1947
return ret;
1948
1949
perf_tool__init(&pdiff.tool, /*ordered_events=*/true);
1950
pdiff.tool.sample = diff__process_sample_event;
1951
pdiff.tool.mmap = perf_event__process_mmap;
1952
pdiff.tool.mmap2 = perf_event__process_mmap2;
1953
pdiff.tool.comm = perf_event__process_comm;
1954
pdiff.tool.exit = perf_event__process_exit;
1955
pdiff.tool.fork = perf_event__process_fork;
1956
pdiff.tool.lost = perf_event__process_lost;
1957
pdiff.tool.namespaces = perf_event__process_namespaces;
1958
pdiff.tool.cgroup = perf_event__process_cgroup;
1959
pdiff.tool.ordering_requires_timestamps = true;
1960
1961
perf_config(diff__config, NULL);
1962
1963
argc = parse_options(argc, argv, options, diff_usage, 0);
1964
1965
if (quiet)
1966
perf_quiet_option();
1967
1968
if (cycles_hist && (compute != COMPUTE_CYCLES))
1969
usage_with_options(diff_usage, options);
1970
1971
if (pdiff.stream)
1972
compute = COMPUTE_STREAM;
1973
1974
symbol__annotation_init();
1975
1976
if (symbol__init(NULL) < 0)
1977
return -1;
1978
1979
if (data_init(argc, argv) < 0)
1980
return -1;
1981
1982
if (check_file_brstack() < 0)
1983
return -1;
1984
1985
if ((compute == COMPUTE_CYCLES || compute == COMPUTE_STREAM)
1986
&& !pdiff.has_br_stack) {
1987
return -1;
1988
}
1989
1990
if (compute == COMPUTE_STREAM) {
1991
symbol_conf.show_branchflag_count = true;
1992
symbol_conf.disable_add2line_warn = true;
1993
callchain_param.mode = CHAIN_FLAT;
1994
callchain_param.key = CCKEY_SRCLINE;
1995
callchain_param.branch_callstack = 1;
1996
symbol_conf.use_callchain = true;
1997
callchain_register_param(&callchain_param);
1998
sort_order = "srcline,symbol,dso";
1999
} else {
2000
if (ui_init() < 0)
2001
return -1;
2002
2003
sort__mode = SORT_MODE__DIFF;
2004
}
2005
2006
if (setup_sorting(/*evlist=*/NULL, perf_session__env(data__files[0].session)) < 0)
2007
usage_with_options(diff_usage, options);
2008
2009
setup_pager();
2010
2011
sort__setup_elide(NULL);
2012
2013
return __cmd_diff();
2014
}
2015
2016