Commit 16c8a109 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar

perf_counter: tools: update the tools to support process and inherited counters

"perf record":
 - per task counter
 - inherit switch
 - nmi switch

"perf report":
 - userspace/kernel filter

"perf stat":
 - userspace/kernel filter
Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
LKML-Reference: <20090505155437.389163017@chello.nl>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 2023b359
...@@ -46,6 +46,9 @@ static int output; ...@@ -46,6 +46,9 @@ static int output;
static char *output_name = "output.perf"; static char *output_name = "output.perf";
static int group = 0; static int group = 0;
static unsigned int realtime_prio = 0; static unsigned int realtime_prio = 0;
static int system_wide = 0;
static int inherit = 1;
static int nmi = 1;
const unsigned int default_count[] = { const unsigned int default_count[] = {
1000000, 1000000,
...@@ -167,7 +170,7 @@ static void display_events_help(void) ...@@ -167,7 +170,7 @@ static void display_events_help(void)
static void display_help(void) static void display_help(void)
{ {
printf( printf(
"Usage: perf-record [<options>]\n" "Usage: perf-record [<options>] <cmd>\n"
"perf-record Options (up to %d event types can be specified at once):\n\n", "perf-record Options (up to %d event types can be specified at once):\n\n",
MAX_COUNTERS); MAX_COUNTERS);
...@@ -178,12 +181,13 @@ static void display_help(void) ...@@ -178,12 +181,13 @@ static void display_help(void)
" -m pages --mmap_pages=<pages> # number of mmap data pages\n" " -m pages --mmap_pages=<pages> # number of mmap data pages\n"
" -o file --output=<file> # output file\n" " -o file --output=<file> # output file\n"
" -r prio --realtime=<prio> # use RT prio\n" " -r prio --realtime=<prio> # use RT prio\n"
" -s --system # system wide profiling\n"
); );
exit(0); exit(0);
} }
static void process_options(int argc, char *argv[]) static void process_options(int argc, const char *argv[])
{ {
int error = 0, counter; int error = 0, counter;
...@@ -196,9 +200,12 @@ static void process_options(int argc, char *argv[]) ...@@ -196,9 +200,12 @@ static void process_options(int argc, char *argv[])
{"mmap_pages", required_argument, NULL, 'm'}, {"mmap_pages", required_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'}, {"output", required_argument, NULL, 'o'},
{"realtime", required_argument, NULL, 'r'}, {"realtime", required_argument, NULL, 'r'},
{"system", no_argument, NULL, 's'},
{"inherit", no_argument, NULL, 'i'},
{"nmi", no_argument, NULL, 'n'},
{NULL, 0, NULL, 0 } {NULL, 0, NULL, 0 }
}; };
int c = getopt_long(argc, argv, "+:c:e:m:o:r:", int c = getopt_long(argc, argv, "+:c:e:m:o:r:sin",
long_options, &option_index); long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
...@@ -209,9 +216,16 @@ static void process_options(int argc, char *argv[]) ...@@ -209,9 +216,16 @@ static void process_options(int argc, char *argv[])
case 'm': mmap_pages = atoi(optarg); break; case 'm': mmap_pages = atoi(optarg); break;
case 'o': output_name = strdup(optarg); break; case 'o': output_name = strdup(optarg); break;
case 'r': realtime_prio = atoi(optarg); break; case 'r': realtime_prio = atoi(optarg); break;
case 's': system_wide ^= 1; break;
case 'i': inherit ^= 1; break;
case 'n': nmi ^= 1; break;
default: error = 1; break; default: error = 1; break;
} }
} }
if (argc - optind == 0)
error = 1;
if (error) if (error)
display_help(); display_help();
...@@ -325,39 +339,27 @@ static void mmap_read(struct mmap_data *md) ...@@ -325,39 +339,27 @@ static void mmap_read(struct mmap_data *md)
static volatile int done = 0; static volatile int done = 0;
static void sigchld_handler(int sig) static void sig_handler(int sig)
{ {
if (sig == SIGCHLD)
done = 1; done = 1;
} }
int cmd_record(int argc, char **argv) static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
{ static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
struct perf_counter_hw_event hw_event;
int i, counter, group_fd, nr_poll = 0;
pid_t pid;
int ret;
page_size = sysconf(_SC_PAGE_SIZE);
process_options(argc, argv); static int nr_poll;
static int nr_cpu;
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); static void open_counters(int cpu)
assert(nr_cpus <= MAX_NR_CPUS); {
assert(nr_cpus >= 0); struct perf_counter_hw_event hw_event;
int counter, group_fd;
output = open(output_name, O_CREAT|O_RDWR, S_IRWXU); int track = 1;
if (output < 0) { pid_t pid = -1;
perror("failed to create output file");
exit(-1);
}
argc -= optind; if (cpu < 0)
argv += optind; pid = 0;
for (i = 0; i < nr_cpus; i++) {
group_fd = -1; group_fd = -1;
for (counter = 0; counter < nr_counters; counter++) { for (counter = 0; counter < nr_counters; counter++) {
...@@ -365,46 +367,81 @@ int cmd_record(int argc, char **argv) ...@@ -365,46 +367,81 @@ int cmd_record(int argc, char **argv)
hw_event.config = event_id[counter]; hw_event.config = event_id[counter];
hw_event.irq_period = event_count[counter]; hw_event.irq_period = event_count[counter];
hw_event.record_type = PERF_RECORD_IP | PERF_RECORD_TID; hw_event.record_type = PERF_RECORD_IP | PERF_RECORD_TID;
hw_event.nmi = 1; hw_event.nmi = nmi;
hw_event.mmap = 1; hw_event.mmap = track;
hw_event.comm = 1; hw_event.comm = track;
hw_event.inherit = (cpu < 0) && inherit;
track = 0; // only the first counter needs these
fd[i][counter] = sys_perf_counter_open(&hw_event, -1, i, group_fd, 0); fd[nr_cpu][counter] =
if (fd[i][counter] < 0) { sys_perf_counter_open(&hw_event, pid, cpu, group_fd, 0);
if (fd[nr_cpu][counter] < 0) {
int err = errno; int err = errno;
printf("kerneltop error: syscall returned with %d (%s)\n", printf("kerneltop error: syscall returned with %d (%s)\n",
fd[i][counter], strerror(err)); fd[nr_cpu][counter], strerror(err));
if (err == EPERM) if (err == EPERM)
printf("Are you root?\n"); printf("Are you root?\n");
exit(-1); exit(-1);
} }
assert(fd[i][counter] >= 0); assert(fd[nr_cpu][counter] >= 0);
fcntl(fd[i][counter], F_SETFL, O_NONBLOCK); fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);
/* /*
* First counter acts as the group leader: * First counter acts as the group leader:
*/ */
if (group && group_fd == -1) if (group && group_fd == -1)
group_fd = fd[i][counter]; group_fd = fd[nr_cpu][counter];
event_array[nr_poll].fd = fd[i][counter]; event_array[nr_poll].fd = fd[nr_cpu][counter];
event_array[nr_poll].events = POLLIN; event_array[nr_poll].events = POLLIN;
nr_poll++; nr_poll++;
mmap_array[i][counter].counter = counter; mmap_array[nr_cpu][counter].counter = counter;
mmap_array[i][counter].prev = 0; mmap_array[nr_cpu][counter].prev = 0;
mmap_array[i][counter].mask = mmap_pages*page_size - 1; mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1;
mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size, mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
PROT_READ, MAP_SHARED, fd[i][counter], 0); PROT_READ, MAP_SHARED, fd[nr_cpu][counter], 0);
if (mmap_array[i][counter].base == MAP_FAILED) { if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {
printf("kerneltop error: failed to mmap with %d (%s)\n", printf("kerneltop error: failed to mmap with %d (%s)\n",
errno, strerror(errno)); errno, strerror(errno));
exit(-1); exit(-1);
} }
} }
nr_cpu++;
}
int cmd_record(int argc, const char **argv)
{
int i, counter;
pid_t pid;
int ret;
page_size = sysconf(_SC_PAGE_SIZE);
process_options(argc, argv);
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
assert(nr_cpus <= MAX_NR_CPUS);
assert(nr_cpus >= 0);
output = open(output_name, O_CREAT|O_RDWR, S_IRWXU);
if (output < 0) {
perror("failed to create output file");
exit(-1);
} }
signal(SIGCHLD, sigchld_handler); argc -= optind;
argv += optind;
if (!system_wide)
open_counters(-1);
else for (i = 0; i < nr_cpus; i++)
open_counters(i);
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
pid = fork(); pid = fork();
if (pid < 0) if (pid < 0)
...@@ -434,7 +471,7 @@ int cmd_record(int argc, char **argv) ...@@ -434,7 +471,7 @@ int cmd_record(int argc, char **argv)
while (!done) { while (!done) {
int hits = events; int hits = events;
for (i = 0; i < nr_cpus; i++) { for (i = 0; i < nr_cpu; i++) {
for (counter = 0; counter < nr_counters; counter++) for (counter = 0; counter < nr_counters; counter++)
mmap_read(&mmap_array[i][counter]); mmap_read(&mmap_array[i][counter]);
} }
......
...@@ -87,6 +87,9 @@ ...@@ -87,6 +87,9 @@
#include "perf.h" #include "perf.h"
#define EVENT_MASK_KERNEL 1
#define EVENT_MASK_USER 2
static int system_wide = 0; static int system_wide = 0;
static int nr_counters = 0; static int nr_counters = 0;
...@@ -104,6 +107,7 @@ static __u64 event_id[MAX_COUNTERS] = { ...@@ -104,6 +107,7 @@ static __u64 event_id[MAX_COUNTERS] = {
static int default_interval = 100000; static int default_interval = 100000;
static int event_count[MAX_COUNTERS]; static int event_count[MAX_COUNTERS];
static int fd[MAX_NR_CPUS][MAX_COUNTERS]; static int fd[MAX_NR_CPUS][MAX_COUNTERS];
static int event_mask[MAX_COUNTERS];
static int tid = -1; static int tid = -1;
static int profile_cpu = -1; static int profile_cpu = -1;
...@@ -258,13 +262,24 @@ static __u64 match_event_symbols(char *str) ...@@ -258,13 +262,24 @@ static __u64 match_event_symbols(char *str)
__u64 config, id; __u64 config, id;
int type; int type;
unsigned int i; unsigned int i;
char mask_str[4];
if (sscanf(str, "r%llx", &config) == 1) if (sscanf(str, "r%llx", &config) == 1)
return config | PERF_COUNTER_RAW_MASK; return config | PERF_COUNTER_RAW_MASK;
if (sscanf(str, "%d:%llu", &type, &id) == 2) switch (sscanf(str, "%d:%llu:%2s", &type, &id, mask_str)) {
case 3:
if (strchr(mask_str, 'u'))
event_mask[nr_counters] |= EVENT_MASK_USER;
if (strchr(mask_str, 'k'))
event_mask[nr_counters] |= EVENT_MASK_KERNEL;
case 2:
return EID(type, id); return EID(type, id);
default:
break;
}
for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
if (!strncmp(str, event_symbols[i].symbol, if (!strncmp(str, event_symbols[i].symbol,
strlen(event_symbols[i].symbol))) strlen(event_symbols[i].symbol)))
...@@ -313,6 +328,11 @@ static void create_perfstat_counter(int counter) ...@@ -313,6 +328,11 @@ static void create_perfstat_counter(int counter)
hw_event.config = event_id[counter]; hw_event.config = event_id[counter];
hw_event.record_type = 0; hw_event.record_type = 0;
hw_event.nmi = 0; hw_event.nmi = 0;
hw_event.exclude_kernel = event_mask[counter] & EVENT_MASK_KERNEL;
hw_event.exclude_user = event_mask[counter] & EVENT_MASK_USER;
printf("exclude: %d\n", event_mask[counter]);
if (scale) if (scale)
hw_event.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | hw_event.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING; PERF_FORMAT_TOTAL_TIME_RUNNING;
......
...@@ -33,8 +33,13 @@ ...@@ -33,8 +33,13 @@
#include <string> #include <string>
#define SHOW_KERNEL 1
#define SHOW_USER 2
#define SHOW_HV 4
static char const *input_name = "output.perf"; static char const *input_name = "output.perf";
static int input; static int input;
static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
static unsigned long page_size; static unsigned long page_size;
static unsigned long mmap_window = 32; static unsigned long mmap_window = 32;
...@@ -359,15 +364,21 @@ static void process_options(int argc, char *argv[]) ...@@ -359,15 +364,21 @@ static void process_options(int argc, char *argv[])
/** Options for getopt */ /** Options for getopt */
static struct option long_options[] = { static struct option long_options[] = {
{"input", required_argument, NULL, 'i'}, {"input", required_argument, NULL, 'i'},
{"no-user", no_argument, NULL, 'u'},
{"no-kernel", no_argument, NULL, 'k'},
{"no-hv", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0 } {NULL, 0, NULL, 0 }
}; };
int c = getopt_long(argc, argv, "+:i:", int c = getopt_long(argc, argv, "+:i:kuh",
long_options, &option_index); long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
switch (c) { switch (c) {
case 'i': input_name = strdup(optarg); break; case 'i': input_name = strdup(optarg); break;
case 'k': show_mask &= ~SHOW_KERNEL; break;
case 'u': show_mask &= ~SHOW_USER; break;
case 'h': show_mask &= ~SHOW_HV; break;
default: error = 1; break; default: error = 1; break;
} }
} }
...@@ -443,22 +454,28 @@ more: ...@@ -443,22 +454,28 @@ more:
if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) { if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
std::string comm, sym, level; std::string comm, sym, level;
int show = 0;
char output[1024]; char output[1024];
if (event->header.misc & PERF_EVENT_MISC_KERNEL) { if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
show |= SHOW_KERNEL;
level = " [k] "; level = " [k] ";
sym = resolve_kernel_symbol(event->ip.ip); sym = resolve_kernel_symbol(event->ip.ip);
} else if (event->header.misc & PERF_EVENT_MISC_USER) { } else if (event->header.misc & PERF_EVENT_MISC_USER) {
show |= SHOW_USER;
level = " [.] "; level = " [.] ";
sym = resolve_user_symbol(event->ip.pid, event->ip.ip); sym = resolve_user_symbol(event->ip.pid, event->ip.ip);
} else { } else {
show |= SHOW_HV;
level = " [H] "; level = " [H] ";
} }
comm = resolve_comm(event->ip.pid);
if (show & show_mask) {
comm = resolve_comm(event->ip.pid);
snprintf(output, sizeof(output), "%16s %s %s", snprintf(output, sizeof(output), "%16s %s %s",
comm.c_str(), level.c_str(), sym.c_str()); comm.c_str(), level.c_str(), sym.c_str());
hist[output]++; hist[output]++;
}
total++; total++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment