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: Peter 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: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Родитель
2023b35921
Коммит
16c8a10932
|
@ -45,7 +45,10 @@ static unsigned int mmap_pages = 16;
|
||||||
static int output;
|
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)
|
||||||
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)
|
||||||
" -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[])
|
||||||
{"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[])
|
||||||
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,18 +339,82 @@ 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];
|
||||||
|
|
||||||
|
static int nr_poll;
|
||||||
|
static int nr_cpu;
|
||||||
|
|
||||||
|
static void open_counters(int cpu)
|
||||||
{
|
{
|
||||||
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;
|
struct perf_counter_hw_event hw_event;
|
||||||
int i, counter, group_fd, nr_poll = 0;
|
int counter, group_fd;
|
||||||
|
int track = 1;
|
||||||
|
pid_t pid = -1;
|
||||||
|
|
||||||
|
if (cpu < 0)
|
||||||
|
pid = 0;
|
||||||
|
|
||||||
|
group_fd = -1;
|
||||||
|
for (counter = 0; counter < nr_counters; counter++) {
|
||||||
|
|
||||||
|
memset(&hw_event, 0, sizeof(hw_event));
|
||||||
|
hw_event.config = event_id[counter];
|
||||||
|
hw_event.irq_period = event_count[counter];
|
||||||
|
hw_event.record_type = PERF_RECORD_IP | PERF_RECORD_TID;
|
||||||
|
hw_event.nmi = nmi;
|
||||||
|
hw_event.mmap = track;
|
||||||
|
hw_event.comm = track;
|
||||||
|
hw_event.inherit = (cpu < 0) && inherit;
|
||||||
|
|
||||||
|
track = 0; // only the first counter needs these
|
||||||
|
|
||||||
|
fd[nr_cpu][counter] =
|
||||||
|
sys_perf_counter_open(&hw_event, pid, cpu, group_fd, 0);
|
||||||
|
|
||||||
|
if (fd[nr_cpu][counter] < 0) {
|
||||||
|
int err = errno;
|
||||||
|
printf("kerneltop error: syscall returned with %d (%s)\n",
|
||||||
|
fd[nr_cpu][counter], strerror(err));
|
||||||
|
if (err == EPERM)
|
||||||
|
printf("Are you root?\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
assert(fd[nr_cpu][counter] >= 0);
|
||||||
|
fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First counter acts as the group leader:
|
||||||
|
*/
|
||||||
|
if (group && group_fd == -1)
|
||||||
|
group_fd = fd[nr_cpu][counter];
|
||||||
|
|
||||||
|
event_array[nr_poll].fd = fd[nr_cpu][counter];
|
||||||
|
event_array[nr_poll].events = POLLIN;
|
||||||
|
nr_poll++;
|
||||||
|
|
||||||
|
mmap_array[nr_cpu][counter].counter = counter;
|
||||||
|
mmap_array[nr_cpu][counter].prev = 0;
|
||||||
|
mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1;
|
||||||
|
mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
|
||||||
|
PROT_READ, MAP_SHARED, fd[nr_cpu][counter], 0);
|
||||||
|
if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {
|
||||||
|
printf("kerneltop error: failed to mmap with %d (%s)\n",
|
||||||
|
errno, strerror(errno));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nr_cpu++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_record(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int i, counter;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -357,54 +435,13 @@ int cmd_record(int argc, char **argv)
|
||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
|
||||||
for (i = 0; i < nr_cpus; i++) {
|
if (!system_wide)
|
||||||
group_fd = -1;
|
open_counters(-1);
|
||||||
for (counter = 0; counter < nr_counters; counter++) {
|
else for (i = 0; i < nr_cpus; i++)
|
||||||
|
open_counters(i);
|
||||||
|
|
||||||
memset(&hw_event, 0, sizeof(hw_event));
|
signal(SIGCHLD, sig_handler);
|
||||||
hw_event.config = event_id[counter];
|
signal(SIGINT, sig_handler);
|
||||||
hw_event.irq_period = event_count[counter];
|
|
||||||
hw_event.record_type = PERF_RECORD_IP | PERF_RECORD_TID;
|
|
||||||
hw_event.nmi = 1;
|
|
||||||
hw_event.mmap = 1;
|
|
||||||
hw_event.comm = 1;
|
|
||||||
|
|
||||||
fd[i][counter] = sys_perf_counter_open(&hw_event, -1, i, group_fd, 0);
|
|
||||||
if (fd[i][counter] < 0) {
|
|
||||||
int err = errno;
|
|
||||||
printf("kerneltop error: syscall returned with %d (%s)\n",
|
|
||||||
fd[i][counter], strerror(err));
|
|
||||||
if (err == EPERM)
|
|
||||||
printf("Are you root?\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
assert(fd[i][counter] >= 0);
|
|
||||||
fcntl(fd[i][counter], F_SETFL, O_NONBLOCK);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* First counter acts as the group leader:
|
|
||||||
*/
|
|
||||||
if (group && group_fd == -1)
|
|
||||||
group_fd = fd[i][counter];
|
|
||||||
|
|
||||||
event_array[nr_poll].fd = fd[i][counter];
|
|
||||||
event_array[nr_poll].events = POLLIN;
|
|
||||||
nr_poll++;
|
|
||||||
|
|
||||||
mmap_array[i][counter].counter = counter;
|
|
||||||
mmap_array[i][counter].prev = 0;
|
|
||||||
mmap_array[i][counter].mask = mmap_pages*page_size - 1;
|
|
||||||
mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
|
|
||||||
PROT_READ, MAP_SHARED, fd[i][counter], 0);
|
|
||||||
if (mmap_array[i][counter].base == MAP_FAILED) {
|
|
||||||
printf("kerneltop error: failed to mmap with %d (%s)\n",
|
|
||||||
errno, strerror(errno));
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signal(SIGCHLD, sigchld_handler);
|
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
|
@ -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 @@
|
||||||
|
|
||||||
#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] = {
|
||||||
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,12 +262,23 @@ 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)) {
|
||||||
return EID(type, id);
|
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);
|
||||||
|
|
||||||
|
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,
|
||||||
|
@ -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 @@
|
||||||
#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[])
|
||||||
/** 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:
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
snprintf(output, sizeof(output), "%16s %s %s",
|
if (show & show_mask) {
|
||||||
comm.c_str(), level.c_str(), sym.c_str());
|
comm = resolve_comm(event->ip.pid);
|
||||||
hist[output]++;
|
snprintf(output, sizeof(output), "%16s %s %s",
|
||||||
|
comm.c_str(), level.c_str(), sym.c_str());
|
||||||
|
hist[output]++;
|
||||||
|
}
|
||||||
|
|
||||||
total++;
|
total++;
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче