aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/perf/Documentation/perf-stat.txt9
-rw-r--r--tools/perf/Makefile13
-rw-r--r--tools/perf/builtin-annotate.c3
-rw-r--r--tools/perf/builtin-diff.c4
-rw-r--r--tools/perf/builtin-evlist.c4
-rw-r--r--tools/perf/builtin-report.c41
-rw-r--r--tools/perf/builtin-stat.c126
-rw-r--r--tools/perf/builtin-top.c3
-rw-r--r--tools/perf/perf.h26
-rw-r--r--tools/perf/scripts/perl/rwtop.pl6
-rw-r--r--tools/perf/tests/hists_link.c3
-rw-r--r--tools/perf/tests/parse-events.c178
-rw-r--r--tools/perf/ui/browsers/hists.c112
-rw-r--r--tools/perf/ui/hist.c53
-rw-r--r--tools/perf/ui/keysyms.h1
-rw-r--r--tools/perf/util/callchain.c2
-rw-r--r--tools/perf/util/cpumap.c54
-rw-r--r--tools/perf/util/cpumap.h9
-rw-r--r--tools/perf/util/evlist.c7
-rw-r--r--tools/perf/util/evlist.h29
-rw-r--r--tools/perf/util/evsel.c2
-rw-r--r--tools/perf/util/evsel.h1
-rw-r--r--tools/perf/util/header.c4
-rw-r--r--tools/perf/util/parse-events.c8
-rw-r--r--tools/perf/util/python-ext-sources1
-rw-r--r--tools/perf/util/sort.c38
-rw-r--r--tools/perf/util/sort.h2
27 files changed, 611 insertions, 128 deletions
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 5289da3344e9..faf4f4feebcc 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -116,9 +116,16 @@ perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- m
116 116
117-I msecs:: 117-I msecs::
118--interval-print msecs:: 118--interval-print msecs::
119 print count deltas every N milliseconds (minimum: 100ms) 119 Print count deltas every N milliseconds (minimum: 100ms)
120 example: perf stat -I 1000 -e cycles -a sleep 5 120 example: perf stat -I 1000 -e cycles -a sleep 5
121 121
122--aggr-socket::
123Aggregate counts per processor socket for system-wide mode measurements. This
124is a useful mode to detect imbalance between sockets. To enable this mode,
125use --aggr-socket in addition to -a. (system-wide). The output includes the
126socket number and the number of online processors on that socket. This is
127useful to gauge the amount of aggregation.
128
122EXAMPLES 129EXAMPLES
123-------- 130--------
124 131
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 4b1044cbd84c..a158309a65ef 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -149,6 +149,8 @@ RM = rm -f
149MKDIR = mkdir 149MKDIR = mkdir
150FIND = find 150FIND = find
151INSTALL = install 151INSTALL = install
152FLEX = flex
153BISON= bison
152 154
153# sparse is architecture-neutral, which means that we need to tell it 155# sparse is architecture-neutral, which means that we need to tell it
154# explicitly what architecture to check for. Fix this up for yours.. 156# explicitly what architecture to check for. Fix this up for yours..
@@ -158,6 +160,14 @@ ifneq ($(MAKECMDGOALS),clean)
158ifneq ($(MAKECMDGOALS),tags) 160ifneq ($(MAKECMDGOALS),tags)
159-include config/feature-tests.mak 161-include config/feature-tests.mak
160 162
163ifeq ($(call get-executable,$(FLEX)),)
164 dummy := $(error Error: $(FLEX) is missing on this system, please install it)
165endif
166
167ifeq ($(call get-executable,$(BISON)),)
168 dummy := $(error Error: $(BISON) is missing on this system, please install it)
169endif
170
161ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) 171ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y)
162 CFLAGS := $(CFLAGS) -fstack-protector-all 172 CFLAGS := $(CFLAGS) -fstack-protector-all
163endif 173endif
@@ -282,9 +292,6 @@ endif
282 292
283export PERL_PATH 293export PERL_PATH
284 294
285FLEX = flex
286BISON= bison
287
288$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c 295$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
289 $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c 296 $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c
290 297
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index dc870cf31b79..95a2ad3f043e 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -309,7 +309,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
309 if (symbol__init() < 0) 309 if (symbol__init() < 0)
310 return -1; 310 return -1;
311 311
312 setup_sorting(annotate_usage, options); 312 if (setup_sorting() < 0)
313 usage_with_options(annotate_usage, options);
313 314
314 if (argc) { 315 if (argc) {
315 /* 316 /*
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 4af0b580b046..d207a97a2db1 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -605,7 +605,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
605 605
606 ui_init(); 606 ui_init();
607 607
608 setup_sorting(diff_usage, options); 608 if (setup_sorting() < 0)
609 usage_with_options(diff_usage, options);
610
609 setup_pager(); 611 setup_pager();
610 612
611 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); 613 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL);
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c
index 85a5e35dd147..05bd9dfe875c 100644
--- a/tools/perf/builtin-evlist.c
+++ b/tools/perf/builtin-evlist.c
@@ -39,7 +39,7 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
39 OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"), 39 OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"),
40 OPT_BOOLEAN('v', "verbose", &details.verbose, 40 OPT_BOOLEAN('v', "verbose", &details.verbose,
41 "Show all event attr details"), 41 "Show all event attr details"),
42 OPT_BOOLEAN('g', "group", &symbol_conf.event_group, 42 OPT_BOOLEAN('g', "group", &details.event_group,
43 "Show event group information"), 43 "Show event group information"),
44 OPT_END() 44 OPT_END()
45 }; 45 };
@@ -52,7 +52,7 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
52 if (argc) 52 if (argc)
53 usage_with_options(evlist_usage, options); 53 usage_with_options(evlist_usage, options);
54 54
55 if (symbol_conf.event_group && (details.verbose || details.freq)) { 55 if (details.event_group && (details.verbose || details.freq)) {
56 pr_err("--group option is not compatible with other options\n"); 56 pr_err("--group option is not compatible with other options\n");
57 usage_with_options(evlist_usage, options); 57 usage_with_options(evlist_usage, options);
58 } 58 }
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 0d221870561a..96b5a7fee4bb 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -468,9 +468,17 @@ static int __cmd_report(struct perf_report *rep)
468 468
469 if (use_browser > 0) { 469 if (use_browser > 0) {
470 if (use_browser == 1) { 470 if (use_browser == 1) {
471 perf_evlist__tui_browse_hists(session->evlist, help, 471 ret = perf_evlist__tui_browse_hists(session->evlist,
472 NULL, 472 help,
473 &session->header.env); 473 NULL,
474 &session->header.env);
475 /*
476 * Usually "ret" is the last pressed key, and we only
477 * care if the key notifies us to switch data file.
478 */
479 if (ret != K_SWITCH_INPUT_DATA)
480 ret = 0;
481
474 } else if (use_browser == 2) { 482 } else if (use_browser == 2) {
475 perf_evlist__gtk_browse_hists(session->evlist, help, 483 perf_evlist__gtk_browse_hists(session->evlist, help,
476 NULL); 484 NULL);
@@ -708,6 +716,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
708 else 716 else
709 input_name = "perf.data"; 717 input_name = "perf.data";
710 } 718 }
719
720 if (strcmp(input_name, "-") != 0)
721 setup_browser(true);
722 else {
723 use_browser = 0;
724 perf_hpp__column_enable(PERF_HPP__OVERHEAD);
725 perf_hpp__init();
726 }
727
728repeat:
711 session = perf_session__new(input_name, O_RDONLY, 729 session = perf_session__new(input_name, O_RDONLY,
712 report.force, false, &report.tool); 730 report.force, false, &report.tool);
713 if (session == NULL) 731 if (session == NULL)
@@ -733,15 +751,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
733 751
734 } 752 }
735 753
736 if (strcmp(input_name, "-") != 0) 754 if (setup_sorting() < 0)
737 setup_browser(true); 755 usage_with_options(report_usage, options);
738 else {
739 use_browser = 0;
740 perf_hpp__column_enable(PERF_HPP__OVERHEAD);
741 perf_hpp__init();
742 }
743
744 setup_sorting(report_usage, options);
745 756
746 /* 757 /*
747 * Only in the newt browser we are doing integrated annotation, 758 * Only in the newt browser we are doing integrated annotation,
@@ -809,6 +820,12 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
809 } 820 }
810 821
811 ret = __cmd_report(&report); 822 ret = __cmd_report(&report);
823 if (ret == K_SWITCH_INPUT_DATA) {
824 perf_session__delete(session);
825 goto repeat;
826 } else
827 ret = 0;
828
812error: 829error:
813 perf_session__delete(session); 830 perf_session__delete(session);
814 return ret; 831 return ret;
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 0368a1036ad6..99848761f573 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -68,6 +68,7 @@
68static void print_stat(int argc, const char **argv); 68static void print_stat(int argc, const char **argv);
69static void print_counter_aggr(struct perf_evsel *counter, char *prefix); 69static void print_counter_aggr(struct perf_evsel *counter, char *prefix);
70static void print_counter(struct perf_evsel *counter, char *prefix); 70static void print_counter(struct perf_evsel *counter, char *prefix);
71static void print_aggr_socket(char *prefix);
71 72
72static struct perf_evlist *evsel_list; 73static struct perf_evlist *evsel_list;
73 74
@@ -79,6 +80,7 @@ static int run_count = 1;
79static bool no_inherit = false; 80static bool no_inherit = false;
80static bool scale = true; 81static bool scale = true;
81static bool no_aggr = false; 82static bool no_aggr = false;
83static bool aggr_socket = false;
82static pid_t child_pid = -1; 84static pid_t child_pid = -1;
83static bool null_run = false; 85static bool null_run = false;
84static int detailed_run = 0; 86static int detailed_run = 0;
@@ -93,6 +95,7 @@ static const char *post_cmd = NULL;
93static bool sync_run = false; 95static bool sync_run = false;
94static unsigned int interval = 0; 96static unsigned int interval = 0;
95static struct timespec ref_time; 97static struct timespec ref_time;
98static struct cpu_map *sock_map;
96 99
97static volatile int done = 0; 100static volatile int done = 0;
98 101
@@ -312,7 +315,9 @@ static void print_interval(void)
312 sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep); 315 sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep);
313 316
314 if (num_print_interval == 0 && !csv_output) { 317 if (num_print_interval == 0 && !csv_output) {
315 if (no_aggr) 318 if (aggr_socket)
319 fprintf(output, "# time socket cpus counts events\n");
320 else if (no_aggr)
316 fprintf(output, "# time CPU counts events\n"); 321 fprintf(output, "# time CPU counts events\n");
317 else 322 else
318 fprintf(output, "# time counts events\n"); 323 fprintf(output, "# time counts events\n");
@@ -321,7 +326,9 @@ static void print_interval(void)
321 if (++num_print_interval == 25) 326 if (++num_print_interval == 25)
322 num_print_interval = 0; 327 num_print_interval = 0;
323 328
324 if (no_aggr) { 329 if (aggr_socket)
330 print_aggr_socket(prefix);
331 else if (no_aggr) {
325 list_for_each_entry(counter, &evsel_list->entries, node) 332 list_for_each_entry(counter, &evsel_list->entries, node)
326 print_counter(counter, prefix); 333 print_counter(counter, prefix);
327 } else { 334 } else {
@@ -349,6 +356,12 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv)
349 ts.tv_nsec = 0; 356 ts.tv_nsec = 0;
350 } 357 }
351 358
359 if (aggr_socket
360 && cpu_map__build_socket_map(evsel_list->cpus, &sock_map)) {
361 perror("cannot build socket map");
362 return -1;
363 }
364
352 if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { 365 if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
353 perror("failed to create pipes"); 366 perror("failed to create pipes");
354 return -1; 367 return -1;
@@ -529,13 +542,21 @@ static void print_noise(struct perf_evsel *evsel, double avg)
529 print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); 542 print_noise_pct(stddev_stats(&ps->res_stats[0]), avg);
530} 543}
531 544
532static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) 545static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
533{ 546{
534 double msecs = avg / 1e6; 547 double msecs = avg / 1e6;
535 char cpustr[16] = { '\0', }; 548 char cpustr[16] = { '\0', };
536 const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s"; 549 const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s";
537 550
538 if (no_aggr) 551 if (aggr_socket)
552 sprintf(cpustr, "S%*d%s%*d%s",
553 csv_output ? 0 : -5,
554 cpu,
555 csv_sep,
556 csv_output ? 0 : 4,
557 nr,
558 csv_sep);
559 else if (no_aggr)
539 sprintf(cpustr, "CPU%*d%s", 560 sprintf(cpustr, "CPU%*d%s",
540 csv_output ? 0 : -4, 561 csv_output ? 0 : -4,
541 perf_evsel__cpus(evsel)->map[cpu], csv_sep); 562 perf_evsel__cpus(evsel)->map[cpu], csv_sep);
@@ -734,7 +755,7 @@ static void print_ll_cache_misses(int cpu,
734 fprintf(output, " of all LL-cache hits "); 755 fprintf(output, " of all LL-cache hits ");
735} 756}
736 757
737static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) 758static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
738{ 759{
739 double total, ratio = 0.0; 760 double total, ratio = 0.0;
740 char cpustr[16] = { '\0', }; 761 char cpustr[16] = { '\0', };
@@ -747,7 +768,15 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
747 else 768 else
748 fmt = "%s%18.0f%s%-25s"; 769 fmt = "%s%18.0f%s%-25s";
749 770
750 if (no_aggr) 771 if (aggr_socket)
772 sprintf(cpustr, "S%*d%s%*d%s",
773 csv_output ? 0 : -5,
774 cpu,
775 csv_sep,
776 csv_output ? 0 : 4,
777 nr,
778 csv_sep);
779 else if (no_aggr)
751 sprintf(cpustr, "CPU%*d%s", 780 sprintf(cpustr, "CPU%*d%s",
752 csv_output ? 0 : -4, 781 csv_output ? 0 : -4,
753 perf_evsel__cpus(evsel)->map[cpu], csv_sep); 782 perf_evsel__cpus(evsel)->map[cpu], csv_sep);
@@ -853,6 +882,70 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
853 } 882 }
854} 883}
855 884
885static void print_aggr_socket(char *prefix)
886{
887 struct perf_evsel *counter;
888 u64 ena, run, val;
889 int cpu, s, s2, sock, nr;
890
891 if (!sock_map)
892 return;
893
894 for (s = 0; s < sock_map->nr; s++) {
895 sock = cpu_map__socket(sock_map, s);
896 list_for_each_entry(counter, &evsel_list->entries, node) {
897 val = ena = run = 0;
898 nr = 0;
899 for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
900 s2 = cpu_map__get_socket(evsel_list->cpus, cpu);
901 if (s2 != sock)
902 continue;
903 val += counter->counts->cpu[cpu].val;
904 ena += counter->counts->cpu[cpu].ena;
905 run += counter->counts->cpu[cpu].run;
906 nr++;
907 }
908 if (prefix)
909 fprintf(output, "%s", prefix);
910
911 if (run == 0 || ena == 0) {
912 fprintf(output, "S%*d%s%*d%s%*s%s%*s",
913 csv_output ? 0 : -5,
914 s,
915 csv_sep,
916 csv_output ? 0 : 4,
917 nr,
918 csv_sep,
919 csv_output ? 0 : 18,
920 counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
921 csv_sep,
922 csv_output ? 0 : -24,
923 perf_evsel__name(counter));
924 if (counter->cgrp)
925 fprintf(output, "%s%s",
926 csv_sep, counter->cgrp->name);
927
928 fputc('\n', output);
929 continue;
930 }
931
932 if (nsec_counter(counter))
933 nsec_printout(sock, nr, counter, val);
934 else
935 abs_printout(sock, nr, counter, val);
936
937 if (!csv_output) {
938 print_noise(counter, 1.0);
939
940 if (run != ena)
941 fprintf(output, " (%.2f%%)",
942 100.0 * run / ena);
943 }
944 fputc('\n', output);
945 }
946 }
947}
948
856/* 949/*
857 * Print out the results of a single counter: 950 * Print out the results of a single counter:
858 * aggregated counts in system-wide mode 951 * aggregated counts in system-wide mode
@@ -882,9 +975,9 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
882 } 975 }
883 976
884 if (nsec_counter(counter)) 977 if (nsec_counter(counter))
885 nsec_printout(-1, counter, avg); 978 nsec_printout(-1, 0, counter, avg);
886 else 979 else
887 abs_printout(-1, counter, avg); 980 abs_printout(-1, 0, counter, avg);
888 981
889 print_noise(counter, avg); 982 print_noise(counter, avg);
890 983
@@ -940,9 +1033,9 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
940 } 1033 }
941 1034
942 if (nsec_counter(counter)) 1035 if (nsec_counter(counter))
943 nsec_printout(cpu, counter, val); 1036 nsec_printout(cpu, 0, counter, val);
944 else 1037 else
945 abs_printout(cpu, counter, val); 1038 abs_printout(cpu, 0, counter, val);
946 1039
947 if (!csv_output) { 1040 if (!csv_output) {
948 print_noise(counter, 1.0); 1041 print_noise(counter, 1.0);
@@ -980,7 +1073,9 @@ static void print_stat(int argc, const char **argv)
980 fprintf(output, ":\n\n"); 1073 fprintf(output, ":\n\n");
981 } 1074 }
982 1075
983 if (no_aggr) { 1076 if (aggr_socket)
1077 print_aggr_socket(NULL);
1078 else if (no_aggr) {
984 list_for_each_entry(counter, &evsel_list->entries, node) 1079 list_for_each_entry(counter, &evsel_list->entries, node)
985 print_counter(counter, NULL); 1080 print_counter(counter, NULL);
986 } else { 1081 } else {
@@ -1228,6 +1323,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
1228 "command to run after to the measured command"), 1323 "command to run after to the measured command"),
1229 OPT_UINTEGER('I', "interval-print", &interval, 1324 OPT_UINTEGER('I', "interval-print", &interval,
1230 "print counts at regular interval in ms (>= 100)"), 1325 "print counts at regular interval in ms (>= 100)"),
1326 OPT_BOOLEAN(0, "aggr-socket", &aggr_socket, "aggregate counts per processor socket"),
1231 OPT_END() 1327 OPT_END()
1232 }; 1328 };
1233 const char * const stat_usage[] = { 1329 const char * const stat_usage[] = {
@@ -1314,6 +1410,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
1314 usage_with_options(stat_usage, options); 1410 usage_with_options(stat_usage, options);
1315 } 1411 }
1316 1412
1413 if (aggr_socket) {
1414 if (!perf_target__has_cpu(&target)) {
1415 fprintf(stderr, "--aggr-socket only available in system-wide mode (-a)\n");
1416 usage_with_options(stat_usage, options);
1417 }
1418 no_aggr = true;
1419 }
1420
1317 if (add_default_attributes()) 1421 if (add_default_attributes())
1318 goto out; 1422 goto out;
1319 1423
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index f561757b1bfa..72f6eb7b4173 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1129,7 +1129,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1129 if (sort_order == default_sort_order) 1129 if (sort_order == default_sort_order)
1130 sort_order = "dso,symbol"; 1130 sort_order = "dso,symbol";
1131 1131
1132 setup_sorting(top_usage, options); 1132 if (setup_sorting() < 0)
1133 usage_with_options(top_usage, options);
1133 1134
1134 if (top.use_stdio) 1135 if (top.use_stdio)
1135 use_browser = 0; 1136 use_browser = 0;
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index 8f3bf388e414..c2206c87fc9f 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -103,32 +103,6 @@
103#include "util/types.h" 103#include "util/types.h"
104#include <stdbool.h> 104#include <stdbool.h>
105 105
106struct perf_mmap {
107 void *base;
108 int mask;
109 unsigned int prev;
110};
111
112static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)
113{
114 struct perf_event_mmap_page *pc = mm->base;
115 int head = pc->data_head;
116 rmb();
117 return head;
118}
119
120static inline void perf_mmap__write_tail(struct perf_mmap *md,
121 unsigned long tail)
122{
123 struct perf_event_mmap_page *pc = md->base;
124
125 /*
126 * ensure all reads are done before we write the tail out.
127 */
128 /* mb(); */
129 pc->data_tail = tail;
130}
131
132/* 106/*
133 * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all 107 * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all
134 * counters in the current task. 108 * counters in the current task.
diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwtop.pl
index 4bb3ecd33472..8b20787021c1 100644
--- a/tools/perf/scripts/perl/rwtop.pl
+++ b/tools/perf/scripts/perl/rwtop.pl
@@ -17,6 +17,7 @@ use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
17use lib "./Perf-Trace-Util/lib"; 17use lib "./Perf-Trace-Util/lib";
18use Perf::Trace::Core; 18use Perf::Trace::Core;
19use Perf::Trace::Util; 19use Perf::Trace::Util;
20use POSIX qw/SIGALRM SA_RESTART/;
20 21
21my $default_interval = 3; 22my $default_interval = 3;
22my $nlines = 20; 23my $nlines = 20;
@@ -90,7 +91,10 @@ sub syscalls::sys_enter_write
90 91
91sub trace_begin 92sub trace_begin
92{ 93{
93 $SIG{ALRM} = \&set_print_pending; 94 my $sa = POSIX::SigAction->new(\&set_print_pending);
95 $sa->flags(SA_RESTART);
96 $sa->safe(1);
97 POSIX::sigaction(SIGALRM, $sa) or die "Can't set SIGALRM handler: $!\n";
94 alarm 1; 98 alarm 1;
95} 99}
96 100
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index 0afd9223bde7..1be64a6c5daf 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -449,7 +449,8 @@ int test__hists_link(void)
449 goto out; 449 goto out;
450 450
451 /* default sort order (comm,dso,sym) will be used */ 451 /* default sort order (comm,dso,sym) will be used */
452 setup_sorting(NULL, NULL); 452 if (setup_sorting() < 0)
453 goto out;
453 454
454 machines__init(&machines); 455 machines__init(&machines);
455 456
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 80a8daf54a63..c5636f36fe31 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -577,7 +577,7 @@ static int test__group2(struct perf_evlist *evlist)
577 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); 577 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
578 TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); 578 TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
579 TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); 579 TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
580 TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); 580 TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
581 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); 581 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
582 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); 582 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
583 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); 583 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
@@ -811,6 +811,166 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
811 return 0; 811 return 0;
812} 812}
813 813
814static int test__group_gh1(struct perf_evlist *evlist)
815{
816 struct perf_evsel *evsel, *leader;
817
818 TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
819 TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
820
821 /* cycles + :H group modifier */
822 evsel = leader = perf_evlist__first(evlist);
823 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
824 TEST_ASSERT_VAL("wrong config",
825 PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
826 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
827 TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
828 TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
829 TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
830 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
831 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
832 TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
833 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
834 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
835 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
836
837 /* cache-misses:G + :H group modifier */
838 evsel = perf_evsel__next(evsel);
839 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
840 TEST_ASSERT_VAL("wrong config",
841 PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
842 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
843 TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
844 TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
845 TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
846 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
847 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
848 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
849 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
850
851 return 0;
852}
853
854static int test__group_gh2(struct perf_evlist *evlist)
855{
856 struct perf_evsel *evsel, *leader;
857
858 TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
859 TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
860
861 /* cycles + :G group modifier */
862 evsel = leader = perf_evlist__first(evlist);
863 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
864 TEST_ASSERT_VAL("wrong config",
865 PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
866 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
867 TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
868 TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
869 TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
870 TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
871 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
872 TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
873 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
874 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
875 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
876
877 /* cache-misses:H + :G group modifier */
878 evsel = perf_evsel__next(evsel);
879 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
880 TEST_ASSERT_VAL("wrong config",
881 PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
882 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
883 TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
884 TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
885 TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
886 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
887 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
888 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
889 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
890
891 return 0;
892}
893
894static int test__group_gh3(struct perf_evlist *evlist)
895{
896 struct perf_evsel *evsel, *leader;
897
898 TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
899 TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
900
901 /* cycles:G + :u group modifier */
902 evsel = leader = perf_evlist__first(evlist);
903 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
904 TEST_ASSERT_VAL("wrong config",
905 PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
906 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
907 TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
908 TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
909 TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
910 TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
911 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
912 TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
913 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
914 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
915 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
916
917 /* cache-misses:H + :u group modifier */
918 evsel = perf_evsel__next(evsel);
919 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
920 TEST_ASSERT_VAL("wrong config",
921 PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
922 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
923 TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
924 TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
925 TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
926 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
927 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
928 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
929 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
930
931 return 0;
932}
933
934static int test__group_gh4(struct perf_evlist *evlist)
935{
936 struct perf_evsel *evsel, *leader;
937
938 TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
939 TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
940
941 /* cycles:G + :uG group modifier */
942 evsel = leader = perf_evlist__first(evlist);
943 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
944 TEST_ASSERT_VAL("wrong config",
945 PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
946 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
947 TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
948 TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
949 TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
950 TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
951 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
952 TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
953 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
954 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
955 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
956
957 /* cache-misses:H + :uG group modifier */
958 evsel = perf_evsel__next(evsel);
959 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
960 TEST_ASSERT_VAL("wrong config",
961 PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
962 TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
963 TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
964 TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
965 TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
966 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
967 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
968 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
969 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
970
971 return 0;
972}
973
814static int count_tracepoints(void) 974static int count_tracepoints(void)
815{ 975{
816 char events_path[PATH_MAX]; 976 char events_path[PATH_MAX];
@@ -1011,6 +1171,22 @@ static struct evlist_test test__events[] = {
1011 .name = "*:*", 1171 .name = "*:*",
1012 .check = test__all_tracepoints, 1172 .check = test__all_tracepoints,
1013 }, 1173 },
1174 [34] = {
1175 .name = "{cycles,cache-misses:G}:H",
1176 .check = test__group_gh1,
1177 },
1178 [35] = {
1179 .name = "{cycles,cache-misses:H}:G",
1180 .check = test__group_gh2,
1181 },
1182 [36] = {
1183 .name = "{cycles:G,cache-misses:H}:u",
1184 .check = test__group_gh3,
1185 },
1186 [37] = {
1187 .name = "{cycles:G,cache-misses:H}:uG",
1188 .check = test__group_gh4,
1189 },
1014}; 1190};
1015 1191
1016static struct evlist_test test__events_pmu[] = { 1192static struct evlist_test test__events_pmu[] = {
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 20ccd57753f7..aa22704047d6 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -1241,6 +1241,96 @@ static inline bool is_report_browser(void *timer)
1241 return timer == NULL; 1241 return timer == NULL;
1242} 1242}
1243 1243
1244/*
1245 * Only runtime switching of perf data file will make "input_name" point
1246 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1247 * whether we need to call free() for current "input_name" during the switch.
1248 */
1249static bool is_input_name_malloced = false;
1250
1251static int switch_data_file(void)
1252{
1253 char *pwd, *options[32], *abs_path[32], *tmp;
1254 DIR *pwd_dir;
1255 int nr_options = 0, choice = -1, ret = -1;
1256 struct dirent *dent;
1257
1258 pwd = getenv("PWD");
1259 if (!pwd)
1260 return ret;
1261
1262 pwd_dir = opendir(pwd);
1263 if (!pwd_dir)
1264 return ret;
1265
1266 memset(options, 0, sizeof(options));
1267 memset(options, 0, sizeof(abs_path));
1268
1269 while ((dent = readdir(pwd_dir))) {
1270 char path[PATH_MAX];
1271 u64 magic;
1272 char *name = dent->d_name;
1273 FILE *file;
1274
1275 if (!(dent->d_type == DT_REG))
1276 continue;
1277
1278 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1279
1280 file = fopen(path, "r");
1281 if (!file)
1282 continue;
1283
1284 if (fread(&magic, 1, 8, file) < 8)
1285 goto close_file_and_continue;
1286
1287 if (is_perf_magic(magic)) {
1288 options[nr_options] = strdup(name);
1289 if (!options[nr_options])
1290 goto close_file_and_continue;
1291
1292 abs_path[nr_options] = strdup(path);
1293 if (!abs_path[nr_options]) {
1294 free(options[nr_options]);
1295 ui__warning("Can't search all data files due to memory shortage.\n");
1296 fclose(file);
1297 break;
1298 }
1299
1300 nr_options++;
1301 }
1302
1303close_file_and_continue:
1304 fclose(file);
1305 if (nr_options >= 32) {
1306 ui__warning("Too many perf data files in PWD!\n"
1307 "Only the first 32 files will be listed.\n");
1308 break;
1309 }
1310 }
1311 closedir(pwd_dir);
1312
1313 if (nr_options) {
1314 choice = ui__popup_menu(nr_options, options);
1315 if (choice < nr_options && choice >= 0) {
1316 tmp = strdup(abs_path[choice]);
1317 if (tmp) {
1318 if (is_input_name_malloced)
1319 free((void *)input_name);
1320 input_name = tmp;
1321 is_input_name_malloced = true;
1322 ret = 0;
1323 } else
1324 ui__warning("Data switch failed due to memory shortage!\n");
1325 }
1326 }
1327
1328 free_popup_options(options, nr_options);
1329 free_popup_options(abs_path, nr_options);
1330 return ret;
1331}
1332
1333
1244static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 1334static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1245 const char *helpline, const char *ev_name, 1335 const char *helpline, const char *ev_name,
1246 bool left_exits, 1336 bool left_exits,
@@ -1275,7 +1365,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1275 int choice = 0, 1365 int choice = 0,
1276 annotate = -2, zoom_dso = -2, zoom_thread = -2, 1366 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1277 annotate_f = -2, annotate_t = -2, browse_map = -2; 1367 annotate_f = -2, annotate_t = -2, browse_map = -2;
1278 int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2; 1368 int scripts_comm = -2, scripts_symbol = -2,
1369 scripts_all = -2, switch_data = -2;
1279 1370
1280 nr_options = 0; 1371 nr_options = 0;
1281 1372
@@ -1332,6 +1423,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1332 if (is_report_browser(hbt)) 1423 if (is_report_browser(hbt))
1333 goto do_scripts; 1424 goto do_scripts;
1334 continue; 1425 continue;
1426 case 's':
1427 if (is_report_browser(hbt))
1428 goto do_data_switch;
1429 continue;
1335 case K_F1: 1430 case K_F1:
1336 case 'h': 1431 case 'h':
1337 case '?': 1432 case '?':
@@ -1351,6 +1446,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1351 "d Zoom into current DSO\n" 1446 "d Zoom into current DSO\n"
1352 "t Zoom into current Thread\n" 1447 "t Zoom into current Thread\n"
1353 "r Run available scripts('perf report' only)\n" 1448 "r Run available scripts('perf report' only)\n"
1449 "s Switch to another data file in PWD ('perf report' only)\n"
1354 "P Print histograms to perf.hist.N\n" 1450 "P Print histograms to perf.hist.N\n"
1355 "V Verbose (DSO names in callchains, etc)\n" 1451 "V Verbose (DSO names in callchains, etc)\n"
1356 "/ Filter symbol by name"); 1452 "/ Filter symbol by name");
@@ -1458,6 +1554,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1458 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) 1554 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1459 scripts_all = nr_options++; 1555 scripts_all = nr_options++;
1460 1556
1557 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1558 "Switch to another data file in PWD") > 0)
1559 switch_data = nr_options++;
1461add_exit_option: 1560add_exit_option:
1462 options[nr_options++] = (char *)"Exit"; 1561 options[nr_options++] = (char *)"Exit";
1463retry_popup_menu: 1562retry_popup_menu:
@@ -1568,6 +1667,16 @@ do_scripts:
1568 1667
1569 script_browse(script_opt); 1668 script_browse(script_opt);
1570 } 1669 }
1670 /* Switch to another data file */
1671 else if (choice == switch_data) {
1672do_data_switch:
1673 if (!switch_data_file()) {
1674 key = K_SWITCH_INPUT_DATA;
1675 break;
1676 } else
1677 ui__warning("Won't switch the data files due to\n"
1678 "no valid data file get selected!\n");
1679 }
1571 } 1680 }
1572out_free_stack: 1681out_free_stack:
1573 pstack__delete(fstack); 1682 pstack__delete(fstack);
@@ -1694,6 +1803,7 @@ browse_hists:
1694 "Do you really want to exit?")) 1803 "Do you really want to exit?"))
1695 continue; 1804 continue;
1696 /* Fall thru */ 1805 /* Fall thru */
1806 case K_SWITCH_INPUT_DATA:
1697 case 'q': 1807 case 'q':
1698 case CTRL('c'): 1808 case CTRL('c'):
1699 goto out; 1809 goto out;
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index a47ce98c2cb1..d671e63aa351 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -9,18 +9,24 @@
9 9
10typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...); 10typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...);
11 11
12static int __hpp__percent_fmt(struct perf_hpp *hpp, struct hist_entry *he, 12static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
13 u64 (*get_field)(struct hist_entry *), 13 u64 (*get_field)(struct hist_entry *),
14 const char *fmt, hpp_snprint_fn print_fn) 14 const char *fmt, hpp_snprint_fn print_fn,
15 bool fmt_percent)
15{ 16{
16 int ret; 17 int ret;
17 double percent = 0.0;
18 struct hists *hists = he->hists; 18 struct hists *hists = he->hists;
19 19
20 if (hists->stats.total_period) 20 if (fmt_percent) {
21 percent = 100.0 * get_field(he) / hists->stats.total_period; 21 double percent = 0.0;
22
23 if (hists->stats.total_period)
24 percent = 100.0 * get_field(he) /
25 hists->stats.total_period;
22 26
23 ret = print_fn(hpp->buf, hpp->size, fmt, percent); 27 ret = print_fn(hpp->buf, hpp->size, fmt, percent);
28 } else
29 ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he));
24 30
25 if (symbol_conf.event_group) { 31 if (symbol_conf.event_group) {
26 int prev_idx, idx_delta; 32 int prev_idx, idx_delta;
@@ -49,11 +55,15 @@ static int __hpp__percent_fmt(struct perf_hpp *hpp, struct hist_entry *he,
49 * have no sample 55 * have no sample
50 */ 56 */
51 ret += print_fn(hpp->buf + ret, hpp->size - ret, 57 ret += print_fn(hpp->buf + ret, hpp->size - ret,
52 fmt, 0.0); 58 fmt, 0);
53 } 59 }
54 60
55 ret += print_fn(hpp->buf + ret, hpp->size - ret, 61 if (fmt_percent)
56 fmt, 100.0 * period / total); 62 ret += print_fn(hpp->buf + ret, hpp->size - ret,
63 fmt, 100.0 * period / total);
64 else
65 ret += print_fn(hpp->buf + ret, hpp->size - ret,
66 fmt, period);
57 67
58 prev_idx = perf_evsel__group_idx(evsel); 68 prev_idx = perf_evsel__group_idx(evsel);
59 } 69 }
@@ -65,23 +75,12 @@ static int __hpp__percent_fmt(struct perf_hpp *hpp, struct hist_entry *he,
65 * zero-fill group members at last which have no sample 75 * zero-fill group members at last which have no sample
66 */ 76 */
67 ret += print_fn(hpp->buf + ret, hpp->size - ret, 77 ret += print_fn(hpp->buf + ret, hpp->size - ret,
68 fmt, 0.0); 78 fmt, 0);
69 } 79 }
70 } 80 }
71 return ret; 81 return ret;
72} 82}
73 83
74static int __hpp__raw_fmt(struct perf_hpp *hpp, struct hist_entry *he,
75 u64 (*get_field)(struct hist_entry *),
76 const char *fmt, hpp_snprint_fn print_fn)
77{
78 int ret;
79
80 ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he));
81 return ret;
82}
83
84
85#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 84#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
86static int hpp__header_##_type(struct perf_hpp *hpp) \ 85static int hpp__header_##_type(struct perf_hpp *hpp) \
87{ \ 86{ \
@@ -116,16 +115,16 @@ static u64 he_get_##_field(struct hist_entry *he) \
116 \ 115 \
117static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ 116static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
118{ \ 117{ \
119 return __hpp__percent_fmt(hpp, he, he_get_##_field, " %6.2f%%", \ 118 return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
120 (hpp_snprint_fn)percent_color_snprintf); \ 119 (hpp_snprint_fn)percent_color_snprintf, true); \
121} 120}
122 121
123#define __HPP_ENTRY_PERCENT_FN(_type, _field) \ 122#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
124static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ 123static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
125{ \ 124{ \
126 const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ 125 const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
127 return __hpp__percent_fmt(hpp, he, he_get_##_field, fmt, \ 126 return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
128 scnprintf); \ 127 scnprintf, true); \
129} 128}
130 129
131#define __HPP_ENTRY_RAW_FN(_type, _field) \ 130#define __HPP_ENTRY_RAW_FN(_type, _field) \
@@ -137,7 +136,7 @@ static u64 he_get_raw_##_field(struct hist_entry *he) \
137static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ 136static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
138{ \ 137{ \
139 const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ 138 const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
140 return __hpp__raw_fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf); \ 139 return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \
141} 140}
142 141
143#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ 142#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
diff --git a/tools/perf/ui/keysyms.h b/tools/perf/ui/keysyms.h
index 809eca5707fa..65092d576b4e 100644
--- a/tools/perf/ui/keysyms.h
+++ b/tools/perf/ui/keysyms.h
@@ -23,5 +23,6 @@
23#define K_TIMER -1 23#define K_TIMER -1
24#define K_ERROR -2 24#define K_ERROR -2
25#define K_RESIZE -3 25#define K_RESIZE -3
26#define K_SWITCH_INPUT_DATA -4
26 27
27#endif /* _PERF_KEYSYMS_H_ */ 28#endif /* _PERF_KEYSYMS_H_ */
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index d3b3f5d82137..42b6a632fe7b 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -444,7 +444,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
444 struct callchain_cursor_node *node = *cursor->last; 444 struct callchain_cursor_node *node = *cursor->last;
445 445
446 if (!node) { 446 if (!node) {
447 node = calloc(sizeof(*node), 1); 447 node = calloc(1, sizeof(*node));
448 if (!node) 448 if (!node)
449 return -ENOMEM; 449 return -ENOMEM;
450 450
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 2b32ffa9ebdb..f817046e22b1 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -1,4 +1,5 @@
1#include "util.h" 1#include "util.h"
2#include "sysfs.h"
2#include "../perf.h" 3#include "../perf.h"
3#include "cpumap.h" 4#include "cpumap.h"
4#include <assert.h> 5#include <assert.h>
@@ -201,3 +202,56 @@ void cpu_map__delete(struct cpu_map *map)
201{ 202{
202 free(map); 203 free(map);
203} 204}
205
206int cpu_map__get_socket(struct cpu_map *map, int idx)
207{
208 FILE *fp;
209 const char *mnt;
210 char path[PATH_MAX];
211 int cpu, ret;
212
213 if (idx > map->nr)
214 return -1;
215
216 cpu = map->map[idx];
217
218 mnt = sysfs_find_mountpoint();
219 if (!mnt)
220 return -1;
221
222 sprintf(path,
223 "%s/devices/system/cpu/cpu%d/topology/physical_package_id",
224 mnt, cpu);
225
226 fp = fopen(path, "r");
227 if (!fp)
228 return -1;
229 ret = fscanf(fp, "%d", &cpu);
230 fclose(fp);
231 return ret == 1 ? cpu : -1;
232}
233
234int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp)
235{
236 struct cpu_map *sock;
237 int nr = cpus->nr;
238 int cpu, s1, s2;
239
240 sock = calloc(1, sizeof(*sock) + nr * sizeof(int));
241 if (!sock)
242 return -1;
243
244 for (cpu = 0; cpu < nr; cpu++) {
245 s1 = cpu_map__get_socket(cpus, cpu);
246 for (s2 = 0; s2 < sock->nr; s2++) {
247 if (s1 == sock->map[s2])
248 break;
249 }
250 if (s2 == sock->nr) {
251 sock->map[sock->nr] = s1;
252 sock->nr++;
253 }
254 }
255 *sockp = sock;
256 return 0;
257}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 2f68a3b8c285..161b00756a12 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -14,6 +14,15 @@ struct cpu_map *cpu_map__dummy_new(void);
14void cpu_map__delete(struct cpu_map *map); 14void cpu_map__delete(struct cpu_map *map);
15struct cpu_map *cpu_map__read(FILE *file); 15struct cpu_map *cpu_map__read(FILE *file);
16size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); 16size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
17int cpu_map__get_socket(struct cpu_map *map, int idx);
18int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp);
19
20static inline int cpu_map__socket(struct cpu_map *sock, int s)
21{
22 if (!sock || s > sock->nr || s < 0)
23 return 0;
24 return sock->map[s];
25}
17 26
18static inline int cpu_map__nr(const struct cpu_map *map) 27static inline int cpu_map__nr(const struct cpu_map *map)
19{ 28{
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index eddd5ebcd690..bc4ad7977438 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -122,8 +122,7 @@ void __perf_evlist__set_leader(struct list_head *list)
122 leader->nr_members = evsel->idx - leader->idx + 1; 122 leader->nr_members = evsel->idx - leader->idx + 1;
123 123
124 list_for_each_entry(evsel, list, node) { 124 list_for_each_entry(evsel, list, node) {
125 if (evsel != leader) 125 evsel->leader = leader;
126 evsel->leader = leader;
127 } 126 }
128} 127}
129 128
@@ -376,7 +375,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
376 if ((old & md->mask) + size != ((old + size) & md->mask)) { 375 if ((old & md->mask) + size != ((old + size) & md->mask)) {
377 unsigned int offset = old; 376 unsigned int offset = old;
378 unsigned int len = min(sizeof(*event), size), cpy; 377 unsigned int len = min(sizeof(*event), size), cpy;
379 void *dst = &evlist->event_copy; 378 void *dst = &md->event_copy;
380 379
381 do { 380 do {
382 cpy = min(md->mask + 1 - (offset & md->mask), len); 381 cpy = min(md->mask + 1 - (offset & md->mask), len);
@@ -386,7 +385,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
386 len -= cpy; 385 len -= cpy;
387 } while (len); 386 } while (len);
388 387
389 event = &evlist->event_copy; 388 event = &md->event_copy;
390 } 389 }
391 390
392 old += size; 391 old += size;
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 73579a25a93e..2dd07bd60b4f 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -17,6 +17,13 @@ struct perf_record_opts;
17#define PERF_EVLIST__HLIST_BITS 8 17#define PERF_EVLIST__HLIST_BITS 8
18#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) 18#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS)
19 19
20struct perf_mmap {
21 void *base;
22 int mask;
23 unsigned int prev;
24 union perf_event event_copy;
25};
26
20struct perf_evlist { 27struct perf_evlist {
21 struct list_head entries; 28 struct list_head entries;
22 struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; 29 struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
@@ -30,7 +37,6 @@ struct perf_evlist {
30 pid_t pid; 37 pid_t pid;
31 } workload; 38 } workload;
32 bool overwrite; 39 bool overwrite;
33 union perf_event event_copy;
34 struct perf_mmap *mmap; 40 struct perf_mmap *mmap;
35 struct pollfd *pollfd; 41 struct pollfd *pollfd;
36 struct thread_map *threads; 42 struct thread_map *threads;
@@ -136,4 +142,25 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist)
136} 142}
137 143
138size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); 144size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp);
145
146static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)
147{
148 struct perf_event_mmap_page *pc = mm->base;
149 int head = pc->data_head;
150 rmb();
151 return head;
152}
153
154static inline void perf_mmap__write_tail(struct perf_mmap *md,
155 unsigned long tail)
156{
157 struct perf_event_mmap_page *pc = md->base;
158
159 /*
160 * ensure all reads are done before we write the tail out.
161 */
162 /* mb(); */
163 pc->data_tail = tail;
164}
165
139#endif /* __PERF_EVLIST_H */ 166#endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index a54701504606..9c82f98f26de 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1391,7 +1391,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
1391 bool first = true; 1391 bool first = true;
1392 int printed = 0; 1392 int printed = 0;
1393 1393
1394 if (symbol_conf.event_group) { 1394 if (details->event_group) {
1395 struct perf_evsel *pos; 1395 struct perf_evsel *pos;
1396 1396
1397 if (!perf_evsel__is_group_leader(evsel)) 1397 if (!perf_evsel__is_group_leader(evsel))
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 8512f6a8a6ea..52021c3087df 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -254,6 +254,7 @@ static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel)
254struct perf_attr_details { 254struct perf_attr_details {
255 bool freq; 255 bool freq;
256 bool verbose; 256 bool verbose;
257 bool event_group;
257}; 258};
258 259
259int perf_evsel__fprintf(struct perf_evsel *evsel, 260int perf_evsel__fprintf(struct perf_evsel *evsel,
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 8022b5254f75..f4bfd79ef6a7 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -2253,7 +2253,7 @@ static int perf_header__adds_write(struct perf_header *header,
2253 if (!nr_sections) 2253 if (!nr_sections)
2254 return 0; 2254 return 0;
2255 2255
2256 feat_sec = p = calloc(sizeof(*feat_sec), nr_sections); 2256 feat_sec = p = calloc(nr_sections, sizeof(*feat_sec));
2257 if (feat_sec == NULL) 2257 if (feat_sec == NULL)
2258 return -ENOMEM; 2258 return -ENOMEM;
2259 2259
@@ -2425,7 +2425,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
2425 if (!nr_sections) 2425 if (!nr_sections)
2426 return 0; 2426 return 0;
2427 2427
2428 feat_sec = sec = calloc(sizeof(*feat_sec), nr_sections); 2428 feat_sec = sec = calloc(nr_sections, sizeof(*feat_sec));
2429 if (!feat_sec) 2429 if (!feat_sec)
2430 return -1; 2430 return -1;
2431 2431
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 4e0f5c2a9fda..c84f48cf9678 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -699,14 +699,6 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
699 int exclude = eu | ek | eh; 699 int exclude = eu | ek | eh;
700 int exclude_GH = evsel ? evsel->exclude_GH : 0; 700 int exclude_GH = evsel ? evsel->exclude_GH : 0;
701 701
702 /*
703 * We are here for group and 'GH' was not set as event
704 * modifier and whatever event/group modifier override
705 * default 'GH' setup.
706 */
707 if (evsel && !exclude_GH)
708 eH = eG = 0;
709
710 memset(mod, 0, sizeof(*mod)); 702 memset(mod, 0, sizeof(*mod));
711 703
712 while (*str) { 704 while (*str) {
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
index c40c2d33199e..64536a993f4a 100644
--- a/tools/perf/util/python-ext-sources
+++ b/tools/perf/util/python-ext-sources
@@ -18,4 +18,5 @@ util/cgroup.c
18util/debugfs.c 18util/debugfs.c
19util/rblist.c 19util/rblist.c
20util/strlist.c 20util/strlist.c
21util/sysfs.c
21../../lib/rbtree.c 22../../lib/rbtree.c
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 83336610faa9..d41926cb9e3f 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -160,9 +160,10 @@ struct sort_entry sort_dso = {
160 160
161/* --sort symbol */ 161/* --sort symbol */
162 162
163static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r, 163static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
164 u64 ip_l, u64 ip_r)
165{ 164{
165 u64 ip_l, ip_r;
166
166 if (!sym_l || !sym_r) 167 if (!sym_l || !sym_r)
167 return cmp_null(sym_l, sym_r); 168 return cmp_null(sym_l, sym_r);
168 169
@@ -178,21 +179,10 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
178static int64_t 179static int64_t
179sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) 180sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
180{ 181{
181 u64 ip_l, ip_r;
182
183 if (!left->ms.sym && !right->ms.sym) 182 if (!left->ms.sym && !right->ms.sym)
184 return right->level - left->level; 183 return right->level - left->level;
185 184
186 if (!left->ms.sym || !right->ms.sym) 185 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
187 return cmp_null(left->ms.sym, right->ms.sym);
188
189 if (left->ms.sym == right->ms.sym)
190 return 0;
191
192 ip_l = left->ms.sym->start;
193 ip_r = right->ms.sym->start;
194
195 return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r);
196} 186}
197 187
198static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, 188static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
@@ -383,8 +373,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
383 if (!from_l->sym && !from_r->sym) 373 if (!from_l->sym && !from_r->sym)
384 return right->level - left->level; 374 return right->level - left->level;
385 375
386 return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr, 376 return _sort__sym_cmp(from_l->sym, from_r->sym);
387 from_r->addr);
388} 377}
389 378
390static int64_t 379static int64_t
@@ -396,7 +385,7 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
396 if (!to_l->sym && !to_r->sym) 385 if (!to_l->sym && !to_r->sym)
397 return right->level - left->level; 386 return right->level - left->level;
398 387
399 return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr); 388 return _sort__sym_cmp(to_l->sym, to_r->sym);
400} 389}
401 390
402static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, 391static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
@@ -576,23 +565,30 @@ int sort_dimension__add(const char *tok)
576 return -ESRCH; 565 return -ESRCH;
577} 566}
578 567
579void setup_sorting(const char * const usagestr[], const struct option *opts) 568int setup_sorting(void)
580{ 569{
581 char *tmp, *tok, *str = strdup(sort_order); 570 char *tmp, *tok, *str = strdup(sort_order);
571 int ret = 0;
572
573 if (str == NULL) {
574 error("Not enough memory to setup sort keys");
575 return -ENOMEM;
576 }
582 577
583 for (tok = strtok_r(str, ", ", &tmp); 578 for (tok = strtok_r(str, ", ", &tmp);
584 tok; tok = strtok_r(NULL, ", ", &tmp)) { 579 tok; tok = strtok_r(NULL, ", ", &tmp)) {
585 int ret = sort_dimension__add(tok); 580 ret = sort_dimension__add(tok);
586 if (ret == -EINVAL) { 581 if (ret == -EINVAL) {
587 error("Invalid --sort key: `%s'", tok); 582 error("Invalid --sort key: `%s'", tok);
588 usage_with_options(usagestr, opts); 583 break;
589 } else if (ret == -ESRCH) { 584 } else if (ret == -ESRCH) {
590 error("Unknown --sort key: `%s'", tok); 585 error("Unknown --sort key: `%s'", tok);
591 usage_with_options(usagestr, opts); 586 break;
592 } 587 }
593 } 588 }
594 589
595 free(str); 590 free(str);
591 return ret;
596} 592}
597 593
598void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, 594void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index e994ad3e9897..b13e56f6ccbe 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -160,7 +160,7 @@ struct sort_entry {
160extern struct sort_entry sort_thread; 160extern struct sort_entry sort_thread;
161extern struct list_head hist_entry__sort_list; 161extern struct list_head hist_entry__sort_list;
162 162
163void setup_sorting(const char * const usagestr[], const struct option *opts); 163int setup_sorting(void);
164extern int sort_dimension__add(const char *); 164extern int sort_dimension__add(const char *);
165void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, 165void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
166 const char *list_name, FILE *fp); 166 const char *list_name, FILE *fp);