Commit c45c6ea2e5c57960dc67e00294c2b78e9540c007
Committed by
Arnaldo Carvalho de Melo
1 parent
761844b9c6
Exists in
master
and in
7 other branches
perf tools: Add the ability to specify list of cpus to monitor
This patch adds a -C option to stat, record, top to designate a list of CPUs to monitor. CPUs can be specified as a comma-separated list or ranges, no space allowed. Examples: $ perf record -a -C0-1,4-7 sleep 1 $ perf top -C0-4 $ perf stat -a -C1,2,3,4 sleep 1 With perf record in per-thread mode with inherit mode on, samples are collected only when the thread runs on the designated CPUs. The -C option does not turn on system-wide mode automatically. Cc: David S. Miller <davem@davemloft.net> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Tom Zanussi <tzanussi@gmail.com> LKML-Reference: <4bff9496.d345d80a.41fe.7b00@mx.google.com> Signed-off-by: Stephane Eranian <eranian@google.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Showing 8 changed files with 110 additions and 24 deletions Side-by-side Diff
tools/perf/Documentation/perf-record.txt
... | ... | @@ -103,6 +103,13 @@ |
103 | 103 | --raw-samples:: |
104 | 104 | Collect raw sample records from all opened counters (default for tracepoint counters). |
105 | 105 | |
106 | +-C:: | |
107 | +--cpu:: | |
108 | +Collect samples only on the list of cpus provided. Multiple CPUs can be provided as a | |
109 | +comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | |
110 | +In per-thread mode with inheritance mode on (default), samples are captured only when | |
111 | +the thread executes on the designated CPUs. Default is to monitor all CPUs. | |
112 | + | |
106 | 113 | SEE ALSO |
107 | 114 | -------- |
108 | 115 | linkperf:perf-stat[1], linkperf:perf-list[1] |
tools/perf/Documentation/perf-stat.txt
... | ... | @@ -46,6 +46,13 @@ |
46 | 46 | -B:: |
47 | 47 | print large numbers with thousands' separators according to locale |
48 | 48 | |
49 | +-C:: | |
50 | +--cpu=:: | |
51 | +Count only on the list of cpus provided. Multiple CPUs can be provided as a | |
52 | +comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | |
53 | +In per-thread mode, this option is ignored. The -a option is still necessary | |
54 | +to activate system-wide monitoring. Default is to count on all CPUs. | |
55 | + | |
49 | 56 | EXAMPLES |
50 | 57 | -------- |
51 | 58 |
tools/perf/Documentation/perf-top.txt
... | ... | @@ -25,9 +25,11 @@ |
25 | 25 | --count=<count>:: |
26 | 26 | Event period to sample. |
27 | 27 | |
28 | --C <cpu>:: | |
29 | ---CPU=<cpu>:: | |
30 | - CPU to profile. | |
28 | +-C <cpu-list>:: | |
29 | +--cpu=<cpu>:: | |
30 | +Monitor only on the list of cpus provided. Multiple CPUs can be provided as a | |
31 | +comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | |
32 | +Default is to monitor all CPUS. | |
31 | 33 | |
32 | 34 | -d <seconds>:: |
33 | 35 | --delay=<seconds>:: |
tools/perf/builtin-record.c
... | ... | @@ -49,7 +49,6 @@ |
49 | 49 | static int realtime_prio = 0; |
50 | 50 | static bool raw_samples = false; |
51 | 51 | static bool system_wide = false; |
52 | -static int profile_cpu = -1; | |
53 | 52 | static pid_t target_pid = -1; |
54 | 53 | static pid_t target_tid = -1; |
55 | 54 | static pid_t *all_tids = NULL; |
... | ... | @@ -74,6 +73,7 @@ |
74 | 73 | static off_t post_processing_offset; |
75 | 74 | |
76 | 75 | static struct perf_session *session; |
76 | +static const char *cpu_list; | |
77 | 77 | |
78 | 78 | struct mmap_data { |
79 | 79 | int counter; |
... | ... | @@ -300,7 +300,7 @@ |
300 | 300 | die("Permission error - are you root?\n" |
301 | 301 | "\t Consider tweaking" |
302 | 302 | " /proc/sys/kernel/perf_event_paranoid.\n"); |
303 | - else if (err == ENODEV && profile_cpu != -1) { | |
303 | + else if (err == ENODEV && cpu_list) { | |
304 | 304 | die("No such device - did you specify" |
305 | 305 | " an out-of-range profile CPU?\n"); |
306 | 306 | } |
307 | 307 | |
... | ... | @@ -622,10 +622,15 @@ |
622 | 622 | close(child_ready_pipe[0]); |
623 | 623 | } |
624 | 624 | |
625 | - if ((!system_wide && no_inherit) || profile_cpu != -1) { | |
626 | - open_counters(profile_cpu); | |
625 | + nr_cpus = read_cpu_map(cpu_list); | |
626 | + if (nr_cpus < 1) { | |
627 | + perror("failed to collect number of CPUs\n"); | |
628 | + return -1; | |
629 | + } | |
630 | + | |
631 | + if (!system_wide && no_inherit && !cpu_list) { | |
632 | + open_counters(-1); | |
627 | 633 | } else { |
628 | - nr_cpus = read_cpu_map(); | |
629 | 634 | for (i = 0; i < nr_cpus; i++) |
630 | 635 | open_counters(cpumap[i]); |
631 | 636 | } |
... | ... | @@ -704,7 +709,7 @@ |
704 | 709 | if (perf_guest) |
705 | 710 | perf_session__process_machines(session, event__synthesize_guest_os); |
706 | 711 | |
707 | - if (!system_wide && profile_cpu == -1) | |
712 | + if (!system_wide && cpu_list) | |
708 | 713 | event__synthesize_thread(target_tid, process_synthesized_event, |
709 | 714 | session); |
710 | 715 | else |
... | ... | @@ -794,8 +799,8 @@ |
794 | 799 | "system-wide collection from all CPUs"), |
795 | 800 | OPT_BOOLEAN('A', "append", &append_file, |
796 | 801 | "append to the output file to do incremental profiling"), |
797 | - OPT_INTEGER('C', "profile_cpu", &profile_cpu, | |
798 | - "CPU to profile on"), | |
802 | + OPT_STRING('C', "cpu", &cpu_list, "cpu", | |
803 | + "list of cpus to monitor"), | |
799 | 804 | OPT_BOOLEAN('f', "force", &force, |
800 | 805 | "overwrite existing data file (deprecated)"), |
801 | 806 | OPT_U64('c', "count", &user_interval, "event period to sample"), |
... | ... | @@ -825,7 +830,7 @@ |
825 | 830 | argc = parse_options(argc, argv, options, record_usage, |
826 | 831 | PARSE_OPT_STOP_AT_NON_OPTION); |
827 | 832 | if (!argc && target_pid == -1 && target_tid == -1 && |
828 | - !system_wide && profile_cpu == -1) | |
833 | + !system_wide && !cpu_list) | |
829 | 834 | usage_with_options(record_usage, options); |
830 | 835 | |
831 | 836 | if (force && append_file) { |
tools/perf/builtin-stat.c
... | ... | @@ -69,7 +69,7 @@ |
69 | 69 | }; |
70 | 70 | |
71 | 71 | static bool system_wide = false; |
72 | -static unsigned int nr_cpus = 0; | |
72 | +static int nr_cpus = 0; | |
73 | 73 | static int run_idx = 0; |
74 | 74 | |
75 | 75 | static int run_count = 1; |
... | ... | @@ -82,6 +82,7 @@ |
82 | 82 | static pid_t child_pid = -1; |
83 | 83 | static bool null_run = false; |
84 | 84 | static bool big_num = false; |
85 | +static const char *cpu_list; | |
85 | 86 | |
86 | 87 | |
87 | 88 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; |
... | ... | @@ -158,7 +159,7 @@ |
158 | 159 | PERF_FORMAT_TOTAL_TIME_RUNNING; |
159 | 160 | |
160 | 161 | if (system_wide) { |
161 | - unsigned int cpu; | |
162 | + int cpu; | |
162 | 163 | |
163 | 164 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
164 | 165 | fd[cpu][counter][0] = sys_perf_event_open(attr, |
... | ... | @@ -208,7 +209,7 @@ |
208 | 209 | static void read_counter(int counter) |
209 | 210 | { |
210 | 211 | u64 count[3], single_count[3]; |
211 | - unsigned int cpu; | |
212 | + int cpu; | |
212 | 213 | size_t res, nv; |
213 | 214 | int scaled; |
214 | 215 | int i, thread; |
... | ... | @@ -542,6 +543,8 @@ |
542 | 543 | "null run - dont start any counters"), |
543 | 544 | OPT_BOOLEAN('B', "big-num", &big_num, |
544 | 545 | "print large numbers with thousands\' separators"), |
546 | + OPT_STRING('C', "cpu", &cpu_list, "cpu", | |
547 | + "list of cpus to monitor in system-wide"), | |
545 | 548 | OPT_END() |
546 | 549 | }; |
547 | 550 | |
548 | 551 | |
... | ... | @@ -566,9 +569,12 @@ |
566 | 569 | } |
567 | 570 | |
568 | 571 | if (system_wide) |
569 | - nr_cpus = read_cpu_map(); | |
572 | + nr_cpus = read_cpu_map(cpu_list); | |
570 | 573 | else |
571 | 574 | nr_cpus = 1; |
575 | + | |
576 | + if (nr_cpus < 1) | |
577 | + usage_with_options(stat_usage, options); | |
572 | 578 | |
573 | 579 | if (target_pid != -1) { |
574 | 580 | target_tid = target_pid; |
tools/perf/builtin-top.c
... | ... | @@ -102,6 +102,7 @@ |
102 | 102 | static int sym_pcnt_filter = 5; |
103 | 103 | static int sym_counter = 0; |
104 | 104 | static int display_weighted = -1; |
105 | +static const char *cpu_list; | |
105 | 106 | |
106 | 107 | /* |
107 | 108 | * Symbols |
... | ... | @@ -1351,8 +1352,8 @@ |
1351 | 1352 | "profile events on existing thread id"), |
1352 | 1353 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1353 | 1354 | "system-wide collection from all CPUs"), |
1354 | - OPT_INTEGER('C', "CPU", &profile_cpu, | |
1355 | - "CPU to profile on"), | |
1355 | + OPT_STRING('C', "cpu", &cpu_list, "cpu", | |
1356 | + "list of cpus to monitor"), | |
1356 | 1357 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1357 | 1358 | "file", "vmlinux pathname"), |
1358 | 1359 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, |
1359 | 1360 | |
... | ... | @@ -1428,10 +1429,10 @@ |
1428 | 1429 | return -ENOMEM; |
1429 | 1430 | |
1430 | 1431 | /* CPU and PID are mutually exclusive */ |
1431 | - if (target_tid > 0 && profile_cpu != -1) { | |
1432 | + if (target_tid > 0 && cpu_list) { | |
1432 | 1433 | printf("WARNING: PID switch overriding CPU\n"); |
1433 | 1434 | sleep(1); |
1434 | - profile_cpu = -1; | |
1435 | + cpu_list = NULL; | |
1435 | 1436 | } |
1436 | 1437 | |
1437 | 1438 | if (!nr_counters) |
1438 | 1439 | |
... | ... | @@ -1469,10 +1470,13 @@ |
1469 | 1470 | attrs[counter].sample_period = default_interval; |
1470 | 1471 | } |
1471 | 1472 | |
1472 | - if (target_tid != -1 || profile_cpu != -1) | |
1473 | + if (target_tid != -1) | |
1473 | 1474 | nr_cpus = 1; |
1474 | 1475 | else |
1475 | - nr_cpus = read_cpu_map(); | |
1476 | + nr_cpus = read_cpu_map(cpu_list); | |
1477 | + | |
1478 | + if (nr_cpus < 1) | |
1479 | + usage_with_options(top_usage, options); | |
1476 | 1480 | |
1477 | 1481 | get_term_dimensions(&winsize); |
1478 | 1482 | if (print_entries == 0) { |
tools/perf/util/cpumap.c
... | ... | @@ -20,7 +20,7 @@ |
20 | 20 | return nr_cpus; |
21 | 21 | } |
22 | 22 | |
23 | -int read_cpu_map(void) | |
23 | +static int read_all_cpu_map(void) | |
24 | 24 | { |
25 | 25 | FILE *onlnf; |
26 | 26 | int nr_cpus = 0; |
... | ... | @@ -56,5 +56,60 @@ |
56 | 56 | return nr_cpus; |
57 | 57 | |
58 | 58 | return default_cpu_map(); |
59 | +} | |
60 | + | |
61 | +int read_cpu_map(const char *cpu_list) | |
62 | +{ | |
63 | + unsigned long start_cpu, end_cpu = 0; | |
64 | + char *p = NULL; | |
65 | + int i, nr_cpus = 0; | |
66 | + | |
67 | + if (!cpu_list) | |
68 | + return read_all_cpu_map(); | |
69 | + | |
70 | + if (!isdigit(*cpu_list)) | |
71 | + goto invalid; | |
72 | + | |
73 | + while (isdigit(*cpu_list)) { | |
74 | + p = NULL; | |
75 | + start_cpu = strtoul(cpu_list, &p, 0); | |
76 | + if (start_cpu >= INT_MAX | |
77 | + || (*p != '\0' && *p != ',' && *p != '-')) | |
78 | + goto invalid; | |
79 | + | |
80 | + if (*p == '-') { | |
81 | + cpu_list = ++p; | |
82 | + p = NULL; | |
83 | + end_cpu = strtoul(cpu_list, &p, 0); | |
84 | + | |
85 | + if (end_cpu >= INT_MAX || (*p != '\0' && *p != ',')) | |
86 | + goto invalid; | |
87 | + | |
88 | + if (end_cpu < start_cpu) | |
89 | + goto invalid; | |
90 | + } else { | |
91 | + end_cpu = start_cpu; | |
92 | + } | |
93 | + | |
94 | + for (; start_cpu <= end_cpu; start_cpu++) { | |
95 | + /* check for duplicates */ | |
96 | + for (i = 0; i < nr_cpus; i++) | |
97 | + if (cpumap[i] == (int)start_cpu) | |
98 | + goto invalid; | |
99 | + | |
100 | + assert(nr_cpus < MAX_NR_CPUS); | |
101 | + cpumap[nr_cpus++] = (int)start_cpu; | |
102 | + } | |
103 | + if (*p) | |
104 | + ++p; | |
105 | + | |
106 | + cpu_list = p; | |
107 | + } | |
108 | + if (nr_cpus > 0) | |
109 | + return nr_cpus; | |
110 | + | |
111 | + return default_cpu_map(); | |
112 | +invalid: | |
113 | + return -1; | |
59 | 114 | } |