提交 c45c6ea2 编写于 作者: S Stephane Eranian 提交者: Arnaldo Carvalho de Melo

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: NStephane Eranian <eranian@google.com>
Signed-off-by: NArnaldo Carvalho de Melo <acme@redhat.com>
上级 761844b9
...@@ -103,6 +103,13 @@ OPTIONS ...@@ -103,6 +103,13 @@ OPTIONS
--raw-samples:: --raw-samples::
Collect raw sample records from all opened counters (default for tracepoint counters). Collect raw sample records from all opened counters (default for tracepoint counters).
-C::
--cpu::
Collect samples only on the list of cpus provided. Multiple CPUs can be provided as a
comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
In per-thread mode with inheritance mode on (default), samples are captured only when
the thread executes on the designated CPUs. Default is to monitor all CPUs.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-list[1] linkperf:perf-stat[1], linkperf:perf-list[1]
...@@ -46,6 +46,13 @@ OPTIONS ...@@ -46,6 +46,13 @@ OPTIONS
-B:: -B::
print large numbers with thousands' separators according to locale print large numbers with thousands' separators according to locale
-C::
--cpu=::
Count only on the list of cpus provided. Multiple CPUs can be provided as a
comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
In per-thread mode, this option is ignored. The -a option is still necessary
to activate system-wide monitoring. Default is to count on all CPUs.
EXAMPLES EXAMPLES
-------- --------
......
...@@ -25,9 +25,11 @@ OPTIONS ...@@ -25,9 +25,11 @@ OPTIONS
--count=<count>:: --count=<count>::
Event period to sample. Event period to sample.
-C <cpu>:: -C <cpu-list>::
--CPU=<cpu>:: --cpu=<cpu>::
CPU to profile. Monitor only on the list of cpus provided. Multiple CPUs can be provided as a
comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
Default is to monitor all CPUS.
-d <seconds>:: -d <seconds>::
--delay=<seconds>:: --delay=<seconds>::
......
...@@ -49,7 +49,6 @@ static int group = 0; ...@@ -49,7 +49,6 @@ static int group = 0;
static int realtime_prio = 0; static int realtime_prio = 0;
static bool raw_samples = false; static bool raw_samples = false;
static bool system_wide = false; static bool system_wide = false;
static int profile_cpu = -1;
static pid_t target_pid = -1; static pid_t target_pid = -1;
static pid_t target_tid = -1; static pid_t target_tid = -1;
static pid_t *all_tids = NULL; static pid_t *all_tids = NULL;
...@@ -74,6 +73,7 @@ static int file_new = 1; ...@@ -74,6 +73,7 @@ static int file_new = 1;
static off_t post_processing_offset; static off_t post_processing_offset;
static struct perf_session *session; static struct perf_session *session;
static const char *cpu_list;
struct mmap_data { struct mmap_data {
int counter; int counter;
...@@ -300,7 +300,7 @@ static void create_counter(int counter, int cpu) ...@@ -300,7 +300,7 @@ static void create_counter(int counter, int cpu)
die("Permission error - are you root?\n" die("Permission error - are you root?\n"
"\t Consider tweaking" "\t Consider tweaking"
" /proc/sys/kernel/perf_event_paranoid.\n"); " /proc/sys/kernel/perf_event_paranoid.\n");
else if (err == ENODEV && profile_cpu != -1) { else if (err == ENODEV && cpu_list) {
die("No such device - did you specify" die("No such device - did you specify"
" an out-of-range profile CPU?\n"); " an out-of-range profile CPU?\n");
} }
...@@ -622,10 +622,15 @@ static int __cmd_record(int argc, const char **argv) ...@@ -622,10 +622,15 @@ static int __cmd_record(int argc, const char **argv)
close(child_ready_pipe[0]); close(child_ready_pipe[0]);
} }
if ((!system_wide && no_inherit) || profile_cpu != -1) { nr_cpus = read_cpu_map(cpu_list);
open_counters(profile_cpu); if (nr_cpus < 1) {
perror("failed to collect number of CPUs\n");
return -1;
}
if (!system_wide && no_inherit && !cpu_list) {
open_counters(-1);
} else { } else {
nr_cpus = read_cpu_map();
for (i = 0; i < nr_cpus; i++) for (i = 0; i < nr_cpus; i++)
open_counters(cpumap[i]); open_counters(cpumap[i]);
} }
...@@ -704,7 +709,7 @@ static int __cmd_record(int argc, const char **argv) ...@@ -704,7 +709,7 @@ static int __cmd_record(int argc, const char **argv)
if (perf_guest) if (perf_guest)
perf_session__process_machines(session, event__synthesize_guest_os); perf_session__process_machines(session, event__synthesize_guest_os);
if (!system_wide && profile_cpu == -1) if (!system_wide && cpu_list)
event__synthesize_thread(target_tid, process_synthesized_event, event__synthesize_thread(target_tid, process_synthesized_event,
session); session);
else else
...@@ -794,8 +799,8 @@ static const struct option options[] = { ...@@ -794,8 +799,8 @@ static const struct option options[] = {
"system-wide collection from all CPUs"), "system-wide collection from all CPUs"),
OPT_BOOLEAN('A', "append", &append_file, OPT_BOOLEAN('A', "append", &append_file,
"append to the output file to do incremental profiling"), "append to the output file to do incremental profiling"),
OPT_INTEGER('C', "profile_cpu", &profile_cpu, OPT_STRING('C', "cpu", &cpu_list, "cpu",
"CPU to profile on"), "list of cpus to monitor"),
OPT_BOOLEAN('f', "force", &force, OPT_BOOLEAN('f', "force", &force,
"overwrite existing data file (deprecated)"), "overwrite existing data file (deprecated)"),
OPT_U64('c', "count", &user_interval, "event period to sample"), OPT_U64('c', "count", &user_interval, "event period to sample"),
...@@ -825,7 +830,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) ...@@ -825,7 +830,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
argc = parse_options(argc, argv, options, record_usage, argc = parse_options(argc, argv, options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && target_pid == -1 && target_tid == -1 && if (!argc && target_pid == -1 && target_tid == -1 &&
!system_wide && profile_cpu == -1) !system_wide && !cpu_list)
usage_with_options(record_usage, options); usage_with_options(record_usage, options);
if (force && append_file) { if (force && append_file) {
......
...@@ -69,7 +69,7 @@ static struct perf_event_attr default_attrs[] = { ...@@ -69,7 +69,7 @@ static struct perf_event_attr default_attrs[] = {
}; };
static bool system_wide = false; static bool system_wide = false;
static unsigned int nr_cpus = 0; static int nr_cpus = 0;
static int run_idx = 0; static int run_idx = 0;
static int run_count = 1; static int run_count = 1;
...@@ -82,6 +82,7 @@ static int thread_num = 0; ...@@ -82,6 +82,7 @@ static int thread_num = 0;
static pid_t child_pid = -1; static pid_t child_pid = -1;
static bool null_run = false; static bool null_run = false;
static bool big_num = false; static bool big_num = false;
static const char *cpu_list;
static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; static int *fd[MAX_NR_CPUS][MAX_COUNTERS];
...@@ -158,7 +159,7 @@ static int create_perf_stat_counter(int counter) ...@@ -158,7 +159,7 @@ static int create_perf_stat_counter(int counter)
PERF_FORMAT_TOTAL_TIME_RUNNING; PERF_FORMAT_TOTAL_TIME_RUNNING;
if (system_wide) { if (system_wide) {
unsigned int cpu; int cpu;
for (cpu = 0; cpu < nr_cpus; cpu++) { for (cpu = 0; cpu < nr_cpus; cpu++) {
fd[cpu][counter][0] = sys_perf_event_open(attr, fd[cpu][counter][0] = sys_perf_event_open(attr,
...@@ -208,7 +209,7 @@ static inline int nsec_counter(int counter) ...@@ -208,7 +209,7 @@ static inline int nsec_counter(int counter)
static void read_counter(int counter) static void read_counter(int counter)
{ {
u64 count[3], single_count[3]; u64 count[3], single_count[3];
unsigned int cpu; int cpu;
size_t res, nv; size_t res, nv;
int scaled; int scaled;
int i, thread; int i, thread;
...@@ -542,6 +543,8 @@ static const struct option options[] = { ...@@ -542,6 +543,8 @@ static const struct option options[] = {
"null run - dont start any counters"), "null run - dont start any counters"),
OPT_BOOLEAN('B', "big-num", &big_num, OPT_BOOLEAN('B', "big-num", &big_num,
"print large numbers with thousands\' separators"), "print large numbers with thousands\' separators"),
OPT_STRING('C', "cpu", &cpu_list, "cpu",
"list of cpus to monitor in system-wide"),
OPT_END() OPT_END()
}; };
...@@ -566,10 +569,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) ...@@ -566,10 +569,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
} }
if (system_wide) if (system_wide)
nr_cpus = read_cpu_map(); nr_cpus = read_cpu_map(cpu_list);
else else
nr_cpus = 1; nr_cpus = 1;
if (nr_cpus < 1)
usage_with_options(stat_usage, options);
if (target_pid != -1) { if (target_pid != -1) {
target_tid = target_pid; target_tid = target_pid;
thread_num = find_all_tid(target_pid, &all_tids); thread_num = find_all_tid(target_pid, &all_tids);
......
...@@ -102,6 +102,7 @@ struct sym_entry *sym_filter_entry_sched = NULL; ...@@ -102,6 +102,7 @@ struct sym_entry *sym_filter_entry_sched = NULL;
static int sym_pcnt_filter = 5; static int sym_pcnt_filter = 5;
static int sym_counter = 0; static int sym_counter = 0;
static int display_weighted = -1; static int display_weighted = -1;
static const char *cpu_list;
/* /*
* Symbols * Symbols
...@@ -1351,8 +1352,8 @@ static const struct option options[] = { ...@@ -1351,8 +1352,8 @@ static const struct option options[] = {
"profile events on existing thread id"), "profile events on existing thread id"),
OPT_BOOLEAN('a', "all-cpus", &system_wide, OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"), "system-wide collection from all CPUs"),
OPT_INTEGER('C', "CPU", &profile_cpu, OPT_STRING('C', "cpu", &cpu_list, "cpu",
"CPU to profile on"), "list of cpus to monitor"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"), "file", "vmlinux pathname"),
OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols,
...@@ -1428,10 +1429,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) ...@@ -1428,10 +1429,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
return -ENOMEM; return -ENOMEM;
/* CPU and PID are mutually exclusive */ /* CPU and PID are mutually exclusive */
if (target_tid > 0 && profile_cpu != -1) { if (target_tid > 0 && cpu_list) {
printf("WARNING: PID switch overriding CPU\n"); printf("WARNING: PID switch overriding CPU\n");
sleep(1); sleep(1);
profile_cpu = -1; cpu_list = NULL;
} }
if (!nr_counters) if (!nr_counters)
...@@ -1469,10 +1470,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) ...@@ -1469,10 +1470,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
attrs[counter].sample_period = default_interval; attrs[counter].sample_period = default_interval;
} }
if (target_tid != -1 || profile_cpu != -1) if (target_tid != -1)
nr_cpus = 1; nr_cpus = 1;
else else
nr_cpus = read_cpu_map(); nr_cpus = read_cpu_map(cpu_list);
if (nr_cpus < 1)
usage_with_options(top_usage, options);
get_term_dimensions(&winsize); get_term_dimensions(&winsize);
if (print_entries == 0) { if (print_entries == 0) {
......
...@@ -20,7 +20,7 @@ static int default_cpu_map(void) ...@@ -20,7 +20,7 @@ static int default_cpu_map(void)
return nr_cpus; return nr_cpus;
} }
int read_cpu_map(void) static int read_all_cpu_map(void)
{ {
FILE *onlnf; FILE *onlnf;
int nr_cpus = 0; int nr_cpus = 0;
...@@ -57,3 +57,58 @@ int read_cpu_map(void) ...@@ -57,3 +57,58 @@ int read_cpu_map(void)
return default_cpu_map(); return default_cpu_map();
} }
int read_cpu_map(const char *cpu_list)
{
unsigned long start_cpu, end_cpu = 0;
char *p = NULL;
int i, nr_cpus = 0;
if (!cpu_list)
return read_all_cpu_map();
if (!isdigit(*cpu_list))
goto invalid;
while (isdigit(*cpu_list)) {
p = NULL;
start_cpu = strtoul(cpu_list, &p, 0);
if (start_cpu >= INT_MAX
|| (*p != '\0' && *p != ',' && *p != '-'))
goto invalid;
if (*p == '-') {
cpu_list = ++p;
p = NULL;
end_cpu = strtoul(cpu_list, &p, 0);
if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
goto invalid;
if (end_cpu < start_cpu)
goto invalid;
} else {
end_cpu = start_cpu;
}
for (; start_cpu <= end_cpu; start_cpu++) {
/* check for duplicates */
for (i = 0; i < nr_cpus; i++)
if (cpumap[i] == (int)start_cpu)
goto invalid;
assert(nr_cpus < MAX_NR_CPUS);
cpumap[nr_cpus++] = (int)start_cpu;
}
if (*p)
++p;
cpu_list = p;
}
if (nr_cpus > 0)
return nr_cpus;
return default_cpu_map();
invalid:
return -1;
}
#ifndef __PERF_CPUMAP_H #ifndef __PERF_CPUMAP_H
#define __PERF_CPUMAP_H #define __PERF_CPUMAP_H
extern int read_cpu_map(void); extern int read_cpu_map(const char *cpu_list);
extern int cpumap[]; extern int cpumap[];
#endif /* __PERF_CPUMAP_H */ #endif /* __PERF_CPUMAP_H */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册