Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+96 −59
Original line number Diff line number Diff line
@@ -46,6 +46,9 @@ static int output;
static char 			*output_name			= "output.perf";
static int			group				= 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[] = {
	1000000,
@@ -167,7 +170,7 @@ static void display_events_help(void)
static void display_help(void)
{
	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",
		 MAX_COUNTERS);

@@ -178,12 +181,13 @@ static void display_help(void)
	" -m pages  --mmap_pages=<pages> # number of mmap data pages\n"
	" -o file   --output=<file>      # output file\n"
	" -r prio   --realtime=<prio>    # use RT prio\n"
	" -s        --system             # system wide profiling\n"
	);

	exit(0);
}

static void process_options(int argc, char *argv[])
static void process_options(int argc, const char *argv[])
{
	int error = 0, counter;

@@ -196,9 +200,12 @@ static void process_options(int argc, char *argv[])
			{"mmap_pages",	required_argument,	NULL, 'm'},
			{"output",	required_argument,	NULL, 'o'},
			{"realtime",	required_argument,	NULL, 'r'},
			{"system",	no_argument,		NULL, 's'},
			{"inherit",	no_argument,		NULL, 'i'},
			{"nmi",		no_argument,		NULL, 'n'},
			{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);
		if (c == -1)
			break;
@@ -209,9 +216,16 @@ static void process_options(int argc, char *argv[])
		case 'm': mmap_pages			=   atoi(optarg); break;
		case 'o': output_name			= strdup(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;
		}
	}

	if (argc - optind == 0)
		error = 1;

	if (error)
		display_help();

@@ -325,39 +339,27 @@ static void mmap_read(struct mmap_data *md)

static volatile int done = 0;

static void sigchld_handler(int sig)
static void sig_handler(int sig)
{
	if (sig == SIGCHLD)
	done = 1;
}

int cmd_record(int argc, char **argv)
{
	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);
static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];

	process_options(argc, argv);
static int nr_poll;
static int nr_cpu;

	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);
	}
static void open_counters(int cpu)
{
	struct perf_counter_hw_event hw_event;
	int counter, group_fd;
	int track = 1;
	pid_t pid = -1;

	argc -= optind;
	argv += optind;
	if (cpu < 0)
		pid = 0;

	for (i = 0; i < nr_cpus; i++) {
	group_fd = -1;
	for (counter = 0; counter < nr_counters; counter++) {

@@ -365,46 +367,81 @@ int cmd_record(int argc, char **argv)
		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		= 1;
			hw_event.mmap		= 1;
			hw_event.comm		= 1;
		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[i][counter] = sys_perf_counter_open(&hw_event, -1, i, group_fd, 0);
			if (fd[i][counter] < 0) {
		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[i][counter], strerror(err));
					fd[nr_cpu][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);
		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[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;
		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) {
		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++;
}

	signal(SIGCHLD, sigchld_handler);
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);
	}

	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();
	if (pid < 0)
@@ -434,7 +471,7 @@ int cmd_record(int argc, char **argv)
	while (!done) {
		int hits = events;

		for (i = 0; i < nr_cpus; i++) {
		for (i = 0; i < nr_cpu; i++) {
			for (counter = 0; counter < nr_counters; counter++)
				mmap_read(&mmap_array[i][counter]);
		}
+22 −2
Original line number Diff line number Diff line
@@ -87,6 +87,9 @@

#include "perf.h"

#define EVENT_MASK_KERNEL		1
#define EVENT_MASK_USER			2

static int			system_wide			=  0;

static int			nr_counters			=  0;
@@ -104,6 +107,7 @@ static __u64 event_id[MAX_COUNTERS] = {
static int			default_interval = 100000;
static int			event_count[MAX_COUNTERS];
static int			fd[MAX_NR_CPUS][MAX_COUNTERS];
static int			event_mask[MAX_COUNTERS];

static int			tid				= -1;
static int			profile_cpu			= -1;
@@ -258,13 +262,24 @@ static __u64 match_event_symbols(char *str)
	__u64 config, id;
	int type;
	unsigned int i;
	char mask_str[4];

	if (sscanf(str, "r%llx", &config) == 1)
		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);

		default:
			break;
	}

	for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
		if (!strncmp(str, event_symbols[i].symbol,
			     strlen(event_symbols[i].symbol)))
@@ -313,6 +328,11 @@ static void create_perfstat_counter(int counter)
	hw_event.config		= event_id[counter];
	hw_event.record_type	= 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)
		hw_event.read_format	= PERF_FORMAT_TOTAL_TIME_ENABLED |
					  PERF_FORMAT_TOTAL_TIME_RUNNING;
+22 −5
Original line number Diff line number Diff line
@@ -33,8 +33,13 @@
#include <string>


#define SHOW_KERNEL	1
#define SHOW_USER	2
#define SHOW_HV		4

static char 		const *input_name = "output.perf";
static int		input;
static int		show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;

static unsigned long	page_size;
static unsigned long	mmap_window = 32;
@@ -359,15 +364,21 @@ static void process_options(int argc, char *argv[])
		/** Options for getopt */
		static struct option long_options[] = {
			{"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 }
		};
		int c = getopt_long(argc, argv, "+:i:",
		int c = getopt_long(argc, argv, "+:i:kuh",
				    long_options, &option_index);
		if (c == -1)
			break;

		switch (c) {
		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;
		}
	}
@@ -443,22 +454,28 @@ more:

	if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
		std::string comm, sym, level;
		int show = 0;
		char output[1024];

		if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
			show |= SHOW_KERNEL;
			level = " [k] ";
			sym = resolve_kernel_symbol(event->ip.ip);
		} else if (event->header.misc & PERF_EVENT_MISC_USER) {
			show |= SHOW_USER;
			level = " [.] ";
			sym = resolve_user_symbol(event->ip.pid, event->ip.ip);
		} else {
			show |= SHOW_HV;
			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",
					comm.c_str(), level.c_str(), sym.c_str());
			hist[output]++;
		}

		total++;