hc
2023-12-08 01573e231f18eb2d99162747186f59511f56b64d
kernel/tools/perf/builtin-diff.c
....@@ -6,6 +6,7 @@
66 * DSOs and symbol information, sort them and produce a diff.
77 */
88 #include "builtin.h"
9
+#include "perf.h"
910
1011 #include "util/debug.h"
1112 #include "util/event.h"
....@@ -15,15 +16,35 @@
1516 #include "util/session.h"
1617 #include "util/tool.h"
1718 #include "util/sort.h"
19
+#include "util/srcline.h"
1820 #include "util/symbol.h"
19
-#include "util/util.h"
2021 #include "util/data.h"
2122 #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 <linux/err.h>
30
+#include <linux/zalloc.h>
31
+#include <subcmd/pager.h>
32
+#include <subcmd/parse-options.h>
2233
2334 #include <errno.h>
2435 #include <inttypes.h>
2536 #include <stdlib.h>
2637 #include <math.h>
38
+
39
+struct perf_diff {
40
+ struct perf_tool tool;
41
+ const char *time_str;
42
+ struct perf_time_interval *ptime_range;
43
+ int range_size;
44
+ int range_num;
45
+ bool has_br_stack;
46
+ bool stream;
47
+};
2748
2849 /* Diff command specific HPP columns. */
2950 enum {
....@@ -35,6 +56,8 @@
3556 PERF_HPP_DIFF__WEIGHTED_DIFF,
3657 PERF_HPP_DIFF__FORMULA,
3758 PERF_HPP_DIFF__DELTA_ABS,
59
+ PERF_HPP_DIFF__CYCLES,
60
+ PERF_HPP_DIFF__CYCLES_HIST,
3861
3962 PERF_HPP_DIFF__MAX_INDEX
4063 };
....@@ -51,6 +74,7 @@
5174 struct perf_data data;
5275 int idx;
5376 struct hists *hists;
77
+ struct evlist_streams *evlist_streams;
5478 struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX];
5579 };
5680
....@@ -69,17 +93,23 @@
6993 static bool show_period;
7094 static bool show_formula;
7195 static bool show_baseline_only;
96
+static bool cycles_hist;
7297 static unsigned int sort_compute = 1;
7398
7499 static s64 compute_wdiff_w1;
75100 static s64 compute_wdiff_w2;
101
+
102
+static const char *cpu_list;
103
+static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
76104
77105 enum {
78106 COMPUTE_DELTA,
79107 COMPUTE_RATIO,
80108 COMPUTE_WEIGHTED_DIFF,
81109 COMPUTE_DELTA_ABS,
110
+ COMPUTE_CYCLES,
82111 COMPUTE_MAX,
112
+ COMPUTE_STREAM, /* After COMPUTE_MAX to avoid use current compute arrays */
83113 };
84114
85115 const char *compute_names[COMPUTE_MAX] = {
....@@ -87,6 +117,7 @@
87117 [COMPUTE_DELTA_ABS] = "delta-abs",
88118 [COMPUTE_RATIO] = "ratio",
89119 [COMPUTE_WEIGHTED_DIFF] = "wdiff",
120
+ [COMPUTE_CYCLES] = "cycles",
90121 };
91122
92123 static int compute = COMPUTE_DELTA_ABS;
....@@ -96,6 +127,7 @@
96127 [COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS,
97128 [COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
98129 [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
130
+ [COMPUTE_CYCLES] = PERF_HPP_DIFF__CYCLES,
99131 };
100132
101133 #define MAX_COL_WIDTH 70
....@@ -134,6 +166,14 @@
134166 [PERF_HPP_DIFF__FORMULA] = {
135167 .name = "Formula",
136168 .width = MAX_COL_WIDTH,
169
+ },
170
+ [PERF_HPP_DIFF__CYCLES] = {
171
+ .name = "[Program Block Range] Cycles Diff",
172
+ .width = 70,
173
+ },
174
+ [PERF_HPP_DIFF__CYCLES_HIST] = {
175
+ .name = "stddev/Hist",
176
+ .width = NUM_SPARKS + 9,
137177 }
138178 };
139179
....@@ -323,15 +363,51 @@
323363 return -1;
324364 }
325365
326
-static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
366
+static void *block_hist_zalloc(size_t size)
367
+{
368
+ struct block_hist *bh;
369
+
370
+ bh = zalloc(size + sizeof(*bh));
371
+ if (!bh)
372
+ return NULL;
373
+
374
+ return &bh->he;
375
+}
376
+
377
+static void block_hist_free(void *he)
378
+{
379
+ struct block_hist *bh;
380
+
381
+ bh = container_of(he, struct block_hist, he);
382
+ hists__delete_entries(&bh->block_hists);
383
+ free(bh);
384
+}
385
+
386
+struct hist_entry_ops block_hist_ops = {
387
+ .new = block_hist_zalloc,
388
+ .free = block_hist_free,
389
+};
390
+
391
+static int diff__process_sample_event(struct perf_tool *tool,
327392 union perf_event *event,
328393 struct perf_sample *sample,
329
- struct perf_evsel *evsel,
394
+ struct evsel *evsel,
330395 struct machine *machine)
331396 {
397
+ struct perf_diff *pdiff = container_of(tool, struct perf_diff, tool);
332398 struct addr_location al;
333399 struct hists *hists = evsel__hists(evsel);
400
+ struct hist_entry_iter iter = {
401
+ .evsel = evsel,
402
+ .sample = sample,
403
+ .ops = &hist_iter_normal,
404
+ };
334405 int ret = -1;
406
+
407
+ if (perf_time__ranges_skip_sample(pdiff->ptime_range, pdiff->range_num,
408
+ sample->time)) {
409
+ return 0;
410
+ }
335411
336412 if (machine__resolve(machine, &al, sample) < 0) {
337413 pr_warning("problem processing %d event, skipping it.\n",
....@@ -339,9 +415,39 @@
339415 return -1;
340416 }
341417
342
- if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) {
343
- pr_warning("problem incrementing symbol period, skipping event\n");
418
+ if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) {
419
+ ret = 0;
344420 goto out_put;
421
+ }
422
+
423
+ switch (compute) {
424
+ case COMPUTE_CYCLES:
425
+ if (!hists__add_entry_ops(hists, &block_hist_ops, &al, NULL,
426
+ NULL, NULL, sample, true)) {
427
+ pr_warning("problem incrementing symbol period, "
428
+ "skipping event\n");
429
+ goto out_put;
430
+ }
431
+
432
+ hist__account_cycles(sample->branch_stack, &al, sample, false,
433
+ NULL);
434
+ break;
435
+
436
+ case COMPUTE_STREAM:
437
+ if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
438
+ NULL)) {
439
+ pr_debug("problem adding hist entry, skipping event\n");
440
+ goto out_put;
441
+ }
442
+ break;
443
+
444
+ default:
445
+ if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample,
446
+ true)) {
447
+ pr_warning("problem incrementing symbol period, "
448
+ "skipping event\n");
449
+ goto out_put;
450
+ }
345451 }
346452
347453 /*
....@@ -359,35 +465,38 @@
359465 return ret;
360466 }
361467
362
-static struct perf_tool tool = {
363
- .sample = diff__process_sample_event,
364
- .mmap = perf_event__process_mmap,
365
- .mmap2 = perf_event__process_mmap2,
366
- .comm = perf_event__process_comm,
367
- .exit = perf_event__process_exit,
368
- .fork = perf_event__process_fork,
369
- .lost = perf_event__process_lost,
370
- .namespaces = perf_event__process_namespaces,
371
- .ordered_events = true,
372
- .ordering_requires_timestamps = true,
468
+static struct perf_diff pdiff = {
469
+ .tool = {
470
+ .sample = diff__process_sample_event,
471
+ .mmap = perf_event__process_mmap,
472
+ .mmap2 = perf_event__process_mmap2,
473
+ .comm = perf_event__process_comm,
474
+ .exit = perf_event__process_exit,
475
+ .fork = perf_event__process_fork,
476
+ .lost = perf_event__process_lost,
477
+ .namespaces = perf_event__process_namespaces,
478
+ .cgroup = perf_event__process_cgroup,
479
+ .ordered_events = true,
480
+ .ordering_requires_timestamps = true,
481
+ },
373482 };
374483
375
-static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
376
- struct perf_evlist *evlist)
484
+static struct evsel *evsel_match(struct evsel *evsel,
485
+ struct evlist *evlist)
377486 {
378
- struct perf_evsel *e;
487
+ struct evsel *e;
379488
380489 evlist__for_each_entry(evlist, e) {
381
- if (perf_evsel__match2(evsel, e))
490
+ if (evsel__match2(evsel, e))
382491 return e;
383492 }
384493
385494 return NULL;
386495 }
387496
388
-static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
497
+static void perf_evlist__collapse_resort(struct evlist *evlist)
389498 {
390
- struct perf_evsel *evsel;
499
+ struct evsel *evsel;
391500
392501 evlist__for_each_entry(evlist, evsel) {
393502 struct hists *hists = evsel__hists(evsel);
....@@ -429,7 +538,7 @@
429538
430539 static void hists__baseline_only(struct hists *hists)
431540 {
432
- struct rb_root *root;
541
+ struct rb_root_cached *root;
433542 struct rb_node *next;
434543
435544 if (hists__has(hists, need_collapse))
....@@ -437,21 +546,143 @@
437546 else
438547 root = hists->entries_in;
439548
440
- next = rb_first(root);
549
+ next = rb_first_cached(root);
441550 while (next != NULL) {
442551 struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
443552
444553 next = rb_next(&he->rb_node_in);
445554 if (!hist_entry__next_pair(he)) {
446
- rb_erase(&he->rb_node_in, root);
555
+ rb_erase_cached(&he->rb_node_in, root);
447556 hist_entry__delete(he);
557
+ }
558
+ }
559
+}
560
+
561
+static int64_t block_cycles_diff_cmp(struct hist_entry *left,
562
+ struct hist_entry *right)
563
+{
564
+ bool pairs_left = hist_entry__has_pairs(left);
565
+ bool pairs_right = hist_entry__has_pairs(right);
566
+ s64 l, r;
567
+
568
+ if (!pairs_left && !pairs_right)
569
+ return 0;
570
+
571
+ l = llabs(left->diff.cycles);
572
+ r = llabs(right->diff.cycles);
573
+ return r - l;
574
+}
575
+
576
+static int64_t block_sort(struct perf_hpp_fmt *fmt __maybe_unused,
577
+ struct hist_entry *left, struct hist_entry *right)
578
+{
579
+ return block_cycles_diff_cmp(right, left);
580
+}
581
+
582
+static void init_block_hist(struct block_hist *bh)
583
+{
584
+ __hists__init(&bh->block_hists, &bh->block_list);
585
+ perf_hpp_list__init(&bh->block_list);
586
+
587
+ INIT_LIST_HEAD(&bh->block_fmt.list);
588
+ INIT_LIST_HEAD(&bh->block_fmt.sort_list);
589
+ bh->block_fmt.cmp = block_info__cmp;
590
+ bh->block_fmt.sort = block_sort;
591
+ perf_hpp_list__register_sort_field(&bh->block_list,
592
+ &bh->block_fmt);
593
+ bh->valid = true;
594
+}
595
+
596
+static struct hist_entry *get_block_pair(struct hist_entry *he,
597
+ struct hists *hists_pair)
598
+{
599
+ struct rb_root_cached *root = hists_pair->entries_in;
600
+ struct rb_node *next = rb_first_cached(root);
601
+ int64_t cmp;
602
+
603
+ while (next != NULL) {
604
+ struct hist_entry *he_pair = rb_entry(next, struct hist_entry,
605
+ rb_node_in);
606
+
607
+ next = rb_next(&he_pair->rb_node_in);
608
+
609
+ cmp = __block_info__cmp(he_pair, he);
610
+ if (!cmp)
611
+ return he_pair;
612
+ }
613
+
614
+ return NULL;
615
+}
616
+
617
+static void init_spark_values(unsigned long *svals, int num)
618
+{
619
+ for (int i = 0; i < num; i++)
620
+ svals[i] = 0;
621
+}
622
+
623
+static void update_spark_value(unsigned long *svals, int num,
624
+ struct stats *stats, u64 val)
625
+{
626
+ int n = stats->n;
627
+
628
+ if (n < num)
629
+ svals[n] = val;
630
+}
631
+
632
+static void compute_cycles_diff(struct hist_entry *he,
633
+ struct hist_entry *pair)
634
+{
635
+ pair->diff.computed = true;
636
+ if (pair->block_info->num && he->block_info->num) {
637
+ pair->diff.cycles =
638
+ pair->block_info->cycles_aggr / pair->block_info->num_aggr -
639
+ he->block_info->cycles_aggr / he->block_info->num_aggr;
640
+
641
+ if (!cycles_hist)
642
+ return;
643
+
644
+ init_stats(&pair->diff.stats);
645
+ init_spark_values(pair->diff.svals, NUM_SPARKS);
646
+
647
+ for (int i = 0; i < pair->block_info->num; i++) {
648
+ u64 val;
649
+
650
+ if (i >= he->block_info->num || i >= NUM_SPARKS)
651
+ break;
652
+
653
+ val = llabs(pair->block_info->cycles_spark[i] -
654
+ he->block_info->cycles_spark[i]);
655
+
656
+ update_spark_value(pair->diff.svals, NUM_SPARKS,
657
+ &pair->diff.stats, val);
658
+ update_stats(&pair->diff.stats, val);
659
+ }
660
+ }
661
+}
662
+
663
+static void block_hists_match(struct hists *hists_base,
664
+ struct hists *hists_pair)
665
+{
666
+ struct rb_root_cached *root = hists_base->entries_in;
667
+ struct rb_node *next = rb_first_cached(root);
668
+
669
+ while (next != NULL) {
670
+ struct hist_entry *he = rb_entry(next, struct hist_entry,
671
+ rb_node_in);
672
+ struct hist_entry *pair = get_block_pair(he, hists_pair);
673
+
674
+ next = rb_next(&he->rb_node_in);
675
+
676
+ if (pair) {
677
+ hist_entry__add_pair(pair, he);
678
+ compute_cycles_diff(he, pair);
448679 }
449680 }
450681 }
451682
452683 static void hists__precompute(struct hists *hists)
453684 {
454
- struct rb_root *root;
685
+ struct rb_root_cached *root;
455686 struct rb_node *next;
456687
457688 if (hists__has(hists, need_collapse))
....@@ -459,14 +690,21 @@
459690 else
460691 root = hists->entries_in;
461692
462
- next = rb_first(root);
693
+ next = rb_first_cached(root);
463694 while (next != NULL) {
695
+ struct block_hist *bh, *pair_bh;
464696 struct hist_entry *he, *pair;
465697 struct data__file *d;
466698 int i;
467699
468700 he = rb_entry(next, struct hist_entry, rb_node_in);
469701 next = rb_next(&he->rb_node_in);
702
+
703
+ if (compute == COMPUTE_CYCLES) {
704
+ bh = container_of(he, struct block_hist, he);
705
+ init_block_hist(bh);
706
+ block_info__process_sym(he, bh, NULL, 0);
707
+ }
470708
471709 data__for_each_file_new(i, d) {
472710 pair = get_pair_data(he, d);
....@@ -483,6 +721,21 @@
483721 break;
484722 case COMPUTE_WEIGHTED_DIFF:
485723 compute_wdiff(he, pair);
724
+ break;
725
+ case COMPUTE_CYCLES:
726
+ pair_bh = container_of(pair, struct block_hist,
727
+ he);
728
+ init_block_hist(pair_bh);
729
+ block_info__process_sym(pair, pair_bh, NULL, 0);
730
+
731
+ bh = container_of(he, struct block_hist, he);
732
+
733
+ if (bh->valid && pair_bh->valid) {
734
+ block_hists_match(&bh->block_hists,
735
+ &pair_bh->block_hists);
736
+ hists__output_resort(&pair_bh->block_hists,
737
+ NULL);
738
+ }
486739 break;
487740 default:
488741 BUG_ON(1);
....@@ -695,6 +948,9 @@
695948 hists__precompute(hists);
696949 hists__output_resort(hists, NULL);
697950
951
+ if (compute == COMPUTE_CYCLES)
952
+ symbol_conf.report_block = true;
953
+
698954 hists__fprintf(hists, !quiet, 0, 0, 0, stdout,
699955 !symbol_conf.use_callchain);
700956 }
....@@ -708,7 +964,7 @@
708964
709965 data__for_each_file(i, d)
710966 fprintf(stdout, "# [%d] %s %s\n",
711
- d->idx, d->data.file.path,
967
+ d->idx, d->data.path,
712968 !d->idx ? "(Baseline)" : "");
713969
714970 fprintf(stdout, "#\n");
....@@ -716,8 +972,8 @@
716972
717973 static void data_process(void)
718974 {
719
- struct perf_evlist *evlist_base = data__files[0].session->evlist;
720
- struct perf_evsel *evsel_base;
975
+ struct evlist *evlist_base = data__files[0].session->evlist;
976
+ struct evsel *evsel_base;
721977 bool first = true;
722978
723979 evlist__for_each_entry(evlist_base, evsel_base) {
....@@ -726,8 +982,8 @@
726982 int i;
727983
728984 data__for_each_file_new(i, d) {
729
- struct perf_evlist *evlist = d->session->evlist;
730
- struct perf_evsel *evsel;
985
+ struct evlist *evlist = d->session->evlist;
986
+ struct evsel *evsel;
731987 struct hists *hists;
732988
733989 evsel = evsel_match(evsel_base, evlist);
....@@ -745,7 +1001,7 @@
7451001
7461002 if (!quiet) {
7471003 fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
748
- perf_evsel__name(evsel_base));
1004
+ evsel__name(evsel_base));
7491005 }
7501006
7511007 first = false;
....@@ -754,15 +1010,60 @@
7541010 data__fprintf();
7551011
7561012 /* Don't sort callchain for perf diff */
757
- perf_evsel__reset_sample_bit(evsel_base, CALLCHAIN);
1013
+ evsel__reset_sample_bit(evsel_base, CALLCHAIN);
7581014
7591015 hists__process(hists_base);
7601016 }
7611017 }
7621018
1019
+static int process_base_stream(struct data__file *data_base,
1020
+ struct data__file *data_pair,
1021
+ const char *title __maybe_unused)
1022
+{
1023
+ struct evlist *evlist_base = data_base->session->evlist;
1024
+ struct evlist *evlist_pair = data_pair->session->evlist;
1025
+ struct evsel *evsel_base, *evsel_pair;
1026
+ struct evsel_streams *es_base, *es_pair;
1027
+
1028
+ evlist__for_each_entry(evlist_base, evsel_base) {
1029
+ evsel_pair = evsel_match(evsel_base, evlist_pair);
1030
+ if (!evsel_pair)
1031
+ continue;
1032
+
1033
+ es_base = evsel_streams__entry(data_base->evlist_streams,
1034
+ evsel_base->idx);
1035
+ if (!es_base)
1036
+ return -1;
1037
+
1038
+ es_pair = evsel_streams__entry(data_pair->evlist_streams,
1039
+ evsel_pair->idx);
1040
+ if (!es_pair)
1041
+ return -1;
1042
+
1043
+ evsel_streams__match(es_base, es_pair);
1044
+ evsel_streams__report(es_base, es_pair);
1045
+ }
1046
+
1047
+ return 0;
1048
+}
1049
+
1050
+static void stream_process(void)
1051
+{
1052
+ /*
1053
+ * Stream comparison only supports two data files.
1054
+ * perf.data.old and perf.data. data__files[0] is perf.data.old,
1055
+ * data__files[1] is perf.data.
1056
+ */
1057
+ process_base_stream(&data__files[0], &data__files[1],
1058
+ "# Output based on old perf data:\n#\n");
1059
+}
1060
+
7631061 static void data__free(struct data__file *d)
7641062 {
7651063 int col;
1064
+
1065
+ if (d->evlist_streams)
1066
+ evlist_streams__delete(d->evlist_streams);
7661067
7671068 for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
7681069 struct diff_hpp_fmt *fmt = &d->fmt[col];
....@@ -771,29 +1072,167 @@
7711072 }
7721073 }
7731074
1075
+static int abstime_str_dup(char **pstr)
1076
+{
1077
+ char *str = NULL;
1078
+
1079
+ if (pdiff.time_str && strchr(pdiff.time_str, ':')) {
1080
+ str = strdup(pdiff.time_str);
1081
+ if (!str)
1082
+ return -ENOMEM;
1083
+ }
1084
+
1085
+ *pstr = str;
1086
+ return 0;
1087
+}
1088
+
1089
+static int parse_absolute_time(struct data__file *d, char **pstr)
1090
+{
1091
+ char *p = *pstr;
1092
+ int ret;
1093
+
1094
+ /*
1095
+ * Absolute timestamp for one file has the format: a.b,c.d
1096
+ * For multiple files, the format is: a.b,c.d:a.b,c.d
1097
+ */
1098
+ p = strchr(*pstr, ':');
1099
+ if (p) {
1100
+ if (p == *pstr) {
1101
+ pr_err("Invalid time string\n");
1102
+ return -EINVAL;
1103
+ }
1104
+
1105
+ *p = 0;
1106
+ p++;
1107
+ if (*p == 0) {
1108
+ pr_err("Invalid time string\n");
1109
+ return -EINVAL;
1110
+ }
1111
+ }
1112
+
1113
+ ret = perf_time__parse_for_ranges(*pstr, d->session,
1114
+ &pdiff.ptime_range,
1115
+ &pdiff.range_size,
1116
+ &pdiff.range_num);
1117
+ if (ret < 0)
1118
+ return ret;
1119
+
1120
+ if (!p || *p == 0)
1121
+ *pstr = NULL;
1122
+ else
1123
+ *pstr = p;
1124
+
1125
+ return ret;
1126
+}
1127
+
1128
+static int parse_percent_time(struct data__file *d)
1129
+{
1130
+ int ret;
1131
+
1132
+ ret = perf_time__parse_for_ranges(pdiff.time_str, d->session,
1133
+ &pdiff.ptime_range,
1134
+ &pdiff.range_size,
1135
+ &pdiff.range_num);
1136
+ return ret;
1137
+}
1138
+
1139
+static int parse_time_str(struct data__file *d, char *abstime_ostr,
1140
+ char **pabstime_tmp)
1141
+{
1142
+ int ret = 0;
1143
+
1144
+ if (abstime_ostr)
1145
+ ret = parse_absolute_time(d, pabstime_tmp);
1146
+ else if (pdiff.time_str)
1147
+ ret = parse_percent_time(d);
1148
+
1149
+ return ret;
1150
+}
1151
+
1152
+static int check_file_brstack(void)
1153
+{
1154
+ struct data__file *d;
1155
+ bool has_br_stack;
1156
+ int i;
1157
+
1158
+ data__for_each_file(i, d) {
1159
+ d->session = perf_session__new(&d->data, false, &pdiff.tool);
1160
+ if (IS_ERR(d->session)) {
1161
+ pr_err("Failed to open %s\n", d->data.path);
1162
+ return PTR_ERR(d->session);
1163
+ }
1164
+
1165
+ has_br_stack = perf_header__has_feat(&d->session->header,
1166
+ HEADER_BRANCH_STACK);
1167
+ perf_session__delete(d->session);
1168
+ if (!has_br_stack)
1169
+ return 0;
1170
+ }
1171
+
1172
+ /* Set only all files having branch stacks */
1173
+ pdiff.has_br_stack = true;
1174
+ return 0;
1175
+}
1176
+
7741177 static int __cmd_diff(void)
7751178 {
7761179 struct data__file *d;
777
- int ret = -EINVAL, i;
1180
+ int ret, i;
1181
+ char *abstime_ostr, *abstime_tmp;
1182
+
1183
+ ret = abstime_str_dup(&abstime_ostr);
1184
+ if (ret)
1185
+ return ret;
1186
+
1187
+ abstime_tmp = abstime_ostr;
1188
+ ret = -EINVAL;
7781189
7791190 data__for_each_file(i, d) {
780
- d->session = perf_session__new(&d->data, false, &tool);
781
- if (!d->session) {
782
- pr_err("Failed to open %s\n", d->data.file.path);
783
- ret = -1;
1191
+ d->session = perf_session__new(&d->data, false, &pdiff.tool);
1192
+ if (IS_ERR(d->session)) {
1193
+ ret = PTR_ERR(d->session);
1194
+ pr_err("Failed to open %s\n", d->data.path);
7841195 goto out_delete;
1196
+ }
1197
+
1198
+ if (pdiff.time_str) {
1199
+ ret = parse_time_str(d, abstime_ostr, &abstime_tmp);
1200
+ if (ret < 0)
1201
+ goto out_delete;
1202
+ }
1203
+
1204
+ if (cpu_list) {
1205
+ ret = perf_session__cpu_bitmap(d->session, cpu_list,
1206
+ cpu_bitmap);
1207
+ if (ret < 0)
1208
+ goto out_delete;
7851209 }
7861210
7871211 ret = perf_session__process_events(d->session);
7881212 if (ret) {
789
- pr_err("Failed to process %s\n", d->data.file.path);
1213
+ pr_err("Failed to process %s\n", d->data.path);
7901214 goto out_delete;
7911215 }
7921216
7931217 perf_evlist__collapse_resort(d->session->evlist);
1218
+
1219
+ if (pdiff.ptime_range)
1220
+ zfree(&pdiff.ptime_range);
1221
+
1222
+ if (compute == COMPUTE_STREAM) {
1223
+ d->evlist_streams = evlist__create_streams(
1224
+ d->session->evlist, 5);
1225
+ if (!d->evlist_streams) {
1226
+ ret = -ENOMEM;
1227
+ goto out_delete;
1228
+ }
1229
+ }
7941230 }
7951231
796
- data_process();
1232
+ if (compute == COMPUTE_STREAM)
1233
+ stream_process();
1234
+ else
1235
+ data_process();
7971236
7981237 out_delete:
7991238 data__for_each_file(i, d) {
....@@ -802,6 +1241,13 @@
8021241 }
8031242
8041243 free(data__files);
1244
+
1245
+ if (pdiff.ptime_range)
1246
+ zfree(&pdiff.ptime_range);
1247
+
1248
+ if (abstime_ostr)
1249
+ free(abstime_ostr);
1250
+
8051251 return ret;
8061252 }
8071253
....@@ -817,13 +1263,16 @@
8171263 OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
8181264 "Show only items with match in baseline"),
8191265 OPT_CALLBACK('c', "compute", &compute,
820
- "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs)",
1266
+ "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs),cycles",
8211267 "Entries differential computation selection",
8221268 setup_compute),
8231269 OPT_BOOLEAN('p', "period", &show_period,
8241270 "Show period values."),
8251271 OPT_BOOLEAN('F', "formula", &show_formula,
8261272 "Show formula."),
1273
+ OPT_BOOLEAN(0, "cycles-hist", &cycles_hist,
1274
+ "Show cycles histogram and standard deviation "
1275
+ "- WARNING: use only with -c cycles."),
8271276 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
8281277 "dump raw trace in ASCII"),
8291278 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
....@@ -849,6 +1298,15 @@
8491298 OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
8501299 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
8511300 "How to display percentage of filtered entries", parse_filter_percentage),
1301
+ OPT_STRING(0, "time", &pdiff.time_str, "str",
1302
+ "Time span (time percent or absolute timestamp)"),
1303
+ OPT_STRING(0, "cpu", &cpu_list, "cpu", "list of cpus to profile"),
1304
+ OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
1305
+ "only consider symbols in these pids"),
1306
+ OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
1307
+ "only consider symbols in these tids"),
1308
+ OPT_BOOLEAN(0, "stream", &pdiff.stream,
1309
+ "Enable hot streams comparison."),
8521310 OPT_END()
8531311 };
8541312
....@@ -888,6 +1346,50 @@
8881346 return ret;
8891347 }
8901348
1349
+static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
1350
+ struct perf_hpp *hpp, int width)
1351
+{
1352
+ struct block_hist *bh = container_of(he, struct block_hist, he);
1353
+ struct block_hist *bh_pair = container_of(pair, struct block_hist, he);
1354
+ struct hist_entry *block_he;
1355
+ struct block_info *bi;
1356
+ char buf[128];
1357
+ char *start_line, *end_line;
1358
+
1359
+ block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx);
1360
+ if (!block_he) {
1361
+ hpp->skip = true;
1362
+ return 0;
1363
+ }
1364
+
1365
+ /*
1366
+ * Avoid printing the warning "addr2line_init failed for ..."
1367
+ */
1368
+ symbol_conf.disable_add2line_warn = true;
1369
+
1370
+ bi = block_he->block_info;
1371
+
1372
+ start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
1373
+ he->ms.sym);
1374
+
1375
+ end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
1376
+ he->ms.sym);
1377
+
1378
+ if ((strncmp(start_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0) &&
1379
+ (strncmp(end_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)) {
1380
+ scnprintf(buf, sizeof(buf), "[%s -> %s] %4ld",
1381
+ start_line, end_line, block_he->diff.cycles);
1382
+ } else {
1383
+ scnprintf(buf, sizeof(buf), "[%7lx -> %7lx] %4ld",
1384
+ bi->start, bi->end, block_he->diff.cycles);
1385
+ }
1386
+
1387
+ free_srcline(start_line);
1388
+ free_srcline(end_line);
1389
+
1390
+ return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1391
+}
1392
+
8911393 static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
8921394 struct perf_hpp *hpp, struct hist_entry *he,
8931395 int comparison_method)
....@@ -899,8 +1401,17 @@
8991401 s64 wdiff;
9001402 char pfmt[20] = " ";
9011403
902
- if (!pair)
1404
+ if (!pair) {
1405
+ if (comparison_method == COMPUTE_CYCLES) {
1406
+ struct block_hist *bh;
1407
+
1408
+ bh = container_of(he, struct block_hist, he);
1409
+ if (bh->block_idx)
1410
+ hpp->skip = true;
1411
+ }
1412
+
9031413 goto no_print;
1414
+ }
9041415
9051416 switch (comparison_method) {
9061417 case COMPUTE_DELTA:
....@@ -935,6 +1446,8 @@
9351446 return color_snprintf(hpp->buf, hpp->size,
9361447 get_percent_color(wdiff),
9371448 pfmt, wdiff);
1449
+ case COMPUTE_CYCLES:
1450
+ return cycles_printf(he, pair, hpp, dfmt->header_width);
9381451 default:
9391452 BUG_ON(1);
9401453 }
....@@ -962,6 +1475,96 @@
9621475 struct perf_hpp *hpp, struct hist_entry *he)
9631476 {
9641477 return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
1478
+}
1479
+
1480
+static int hpp__color_cycles(struct perf_hpp_fmt *fmt,
1481
+ struct perf_hpp *hpp, struct hist_entry *he)
1482
+{
1483
+ return __hpp__color_compare(fmt, hpp, he, COMPUTE_CYCLES);
1484
+}
1485
+
1486
+static int all_zero(unsigned long *vals, int len)
1487
+{
1488
+ int i;
1489
+
1490
+ for (i = 0; i < len; i++)
1491
+ if (vals[i] != 0)
1492
+ return 0;
1493
+ return 1;
1494
+}
1495
+
1496
+static int print_cycles_spark(char *bf, int size, unsigned long *svals, u64 n)
1497
+{
1498
+ int printed;
1499
+
1500
+ if (n <= 1)
1501
+ return 0;
1502
+
1503
+ if (n > NUM_SPARKS)
1504
+ n = NUM_SPARKS;
1505
+ if (all_zero(svals, n))
1506
+ return 0;
1507
+
1508
+ printed = print_spark(bf, size, svals, n);
1509
+ printed += scnprintf(bf + printed, size - printed, " ");
1510
+ return printed;
1511
+}
1512
+
1513
+static int hpp__color_cycles_hist(struct perf_hpp_fmt *fmt,
1514
+ struct perf_hpp *hpp, struct hist_entry *he)
1515
+{
1516
+ struct diff_hpp_fmt *dfmt =
1517
+ container_of(fmt, struct diff_hpp_fmt, fmt);
1518
+ struct hist_entry *pair = get_pair_fmt(he, dfmt);
1519
+ struct block_hist *bh = container_of(he, struct block_hist, he);
1520
+ struct block_hist *bh_pair;
1521
+ struct hist_entry *block_he;
1522
+ char spark[32], buf[128];
1523
+ double r;
1524
+ int ret, pad;
1525
+
1526
+ if (!pair) {
1527
+ if (bh->block_idx)
1528
+ hpp->skip = true;
1529
+
1530
+ goto no_print;
1531
+ }
1532
+
1533
+ bh_pair = container_of(pair, struct block_hist, he);
1534
+
1535
+ block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx);
1536
+ if (!block_he) {
1537
+ hpp->skip = true;
1538
+ goto no_print;
1539
+ }
1540
+
1541
+ ret = print_cycles_spark(spark, sizeof(spark), block_he->diff.svals,
1542
+ block_he->diff.stats.n);
1543
+
1544
+ r = rel_stddev_stats(stddev_stats(&block_he->diff.stats),
1545
+ avg_stats(&block_he->diff.stats));
1546
+
1547
+ if (ret) {
1548
+ /*
1549
+ * Padding spaces if number of sparks less than NUM_SPARKS
1550
+ * otherwise the output is not aligned.
1551
+ */
1552
+ pad = NUM_SPARKS - ((ret - 1) / 3);
1553
+ scnprintf(buf, sizeof(buf), "%s%5.1f%% %s", "\u00B1", r, spark);
1554
+ ret = scnprintf(hpp->buf, hpp->size, "%*s",
1555
+ dfmt->header_width, buf);
1556
+
1557
+ if (pad) {
1558
+ ret += scnprintf(hpp->buf + ret, hpp->size - ret,
1559
+ "%-*s", pad, " ");
1560
+ }
1561
+
1562
+ return ret;
1563
+ }
1564
+
1565
+no_print:
1566
+ return scnprintf(hpp->buf, hpp->size, "%*s",
1567
+ dfmt->header_width, " ");
9651568 }
9661569
9671570 static void
....@@ -1038,7 +1641,7 @@
10381641
10391642 default:
10401643 BUG_ON(1);
1041
- };
1644
+ }
10421645 }
10431646
10441647 static void
....@@ -1165,6 +1768,14 @@
11651768 fmt->color = hpp__color_delta;
11661769 fmt->sort = hist_entry__cmp_delta_abs;
11671770 break;
1771
+ case PERF_HPP_DIFF__CYCLES:
1772
+ fmt->color = hpp__color_cycles;
1773
+ fmt->sort = hist_entry__cmp_nop;
1774
+ break;
1775
+ case PERF_HPP_DIFF__CYCLES_HIST:
1776
+ fmt->color = hpp__color_cycles_hist;
1777
+ fmt->sort = hist_entry__cmp_nop;
1778
+ break;
11681779 default:
11691780 fmt->sort = hist_entry__cmp_nop;
11701781 break;
....@@ -1190,9 +1801,13 @@
11901801 * PERF_HPP_DIFF__DELTA
11911802 * PERF_HPP_DIFF__RATIO
11921803 * PERF_HPP_DIFF__WEIGHTED_DIFF
1804
+ * PERF_HPP_DIFF__CYCLES
11931805 */
11941806 data__hpp_register(d, i ? compute_2_hpp[compute] :
11951807 PERF_HPP_DIFF__BASELINE);
1808
+
1809
+ if (cycles_hist && i)
1810
+ data__hpp_register(d, PERF_HPP_DIFF__CYCLES_HIST);
11961811
11971812 /*
11981813 * And the rest:
....@@ -1245,6 +1860,13 @@
12451860 case COMPUTE_DELTA_ABS:
12461861 fmt->sort = hist_entry__cmp_delta_abs_idx;
12471862 break;
1863
+ case COMPUTE_CYCLES:
1864
+ /*
1865
+ * Should set since 'fmt->sort' is called without
1866
+ * checking valid during sorting
1867
+ */
1868
+ fmt->sort = hist_entry__cmp_nop;
1869
+ break;
12481870 default:
12491871 BUG_ON(1);
12501872 }
....@@ -1289,9 +1911,9 @@
12891911 data__for_each_file(i, d) {
12901912 struct perf_data *data = &d->data;
12911913
1292
- data->file.path = use_default ? defaults[i] : argv[i];
1293
- data->mode = PERF_DATA_MODE_READ,
1294
- data->force = force,
1914
+ data->path = use_default ? defaults[i] : argv[i];
1915
+ data->mode = PERF_DATA_MODE_READ,
1916
+ data->force = force,
12951917
12961918 d->idx = i;
12971919 }
....@@ -1341,16 +1963,43 @@
13411963 if (quiet)
13421964 perf_quiet_option();
13431965
1966
+ if (cycles_hist && (compute != COMPUTE_CYCLES))
1967
+ usage_with_options(diff_usage, options);
1968
+
1969
+ if (pdiff.stream)
1970
+ compute = COMPUTE_STREAM;
1971
+
1972
+ symbol__annotation_init();
1973
+
13441974 if (symbol__init(NULL) < 0)
13451975 return -1;
13461976
13471977 if (data_init(argc, argv) < 0)
13481978 return -1;
13491979
1350
- if (ui_init() < 0)
1980
+ if (check_file_brstack() < 0)
13511981 return -1;
13521982
1353
- sort__mode = SORT_MODE__DIFF;
1983
+ if ((compute == COMPUTE_CYCLES || compute == COMPUTE_STREAM)
1984
+ && !pdiff.has_br_stack) {
1985
+ return -1;
1986
+ }
1987
+
1988
+ if (compute == COMPUTE_STREAM) {
1989
+ symbol_conf.show_branchflag_count = true;
1990
+ symbol_conf.disable_add2line_warn = true;
1991
+ callchain_param.mode = CHAIN_FLAT;
1992
+ callchain_param.key = CCKEY_SRCLINE;
1993
+ callchain_param.branch_callstack = 1;
1994
+ symbol_conf.use_callchain = true;
1995
+ callchain_register_param(&callchain_param);
1996
+ sort_order = "srcline,symbol,dso";
1997
+ } else {
1998
+ if (ui_init() < 0)
1999
+ return -1;
2000
+
2001
+ sort__mode = SORT_MODE__DIFF;
2002
+ }
13542003
13552004 if (setup_sorting(NULL) < 0)
13562005 usage_with_options(diff_usage, options);