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

Commit 1a853e36 authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by Ingo Molnar
Browse files

perf record: Allow specifying a pid to record



Allow specifying a pid instead of always fork+exec'ing a command.

Because the PERF_EVENT_COMM and PERF_EVENT_MMAP events happened before
we connected, we must synthesize them so that 'perf report' can get what
it needs.

[ Impact: add new command line option ]

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Cc: Clark Williams <williams@redhat.com>
Cc: John Kacur <jkacur@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20090515015046.GA13664@ghostprotocols.net>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 5bb9efe3
Loading
Loading
Loading
Loading
+146 −17
Original line number Original line Diff line number Diff line
@@ -34,6 +34,9 @@


#include "perf.h"
#include "perf.h"


#define ALIGN(x,a)		__ALIGN_MASK(x,(typeof(x))(a)-1)
#define __ALIGN_MASK(x,mask)	(((x)+(mask))&~(mask))

static int			nr_counters			=  0;
static int			nr_counters			=  0;
static __u64			event_id[MAX_COUNTERS]		= { };
static __u64			event_id[MAX_COUNTERS]		= { };
static int			default_interval = 100000;
static int			default_interval = 100000;
@@ -47,6 +50,7 @@ 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			system_wide			= 0;
static pid_t			target_pid			= -1;
static int			inherit				= 1;
static int			inherit				= 1;
static int			nmi				= 1;
static int			nmi				= 1;


@@ -180,6 +184,7 @@ static void display_help(void)
	" -c CNT    --count=CNT          # event period to sample\n"
	" -c CNT    --count=CNT          # event period to sample\n"
	" -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"
	" -p pid    --pid=<pid>		 # record events on existing pid\n"
	" -r prio   --realtime=<prio>    # use RT prio\n"
	" -r prio   --realtime=<prio>    # use RT prio\n"
	" -s        --system             # system wide profiling\n"
	" -s        --system             # system wide profiling\n"
	);
	);
@@ -199,13 +204,14 @@ static void process_options(int argc, const char *argv[])
			{"event",	required_argument,	NULL, 'e'},
			{"event",	required_argument,	NULL, 'e'},
			{"mmap_pages",	required_argument,	NULL, 'm'},
			{"mmap_pages",	required_argument,	NULL, 'm'},
			{"output",	required_argument,	NULL, 'o'},
			{"output",	required_argument,	NULL, 'o'},
			{"pid",		required_argument,	NULL, 'p'},
			{"realtime",	required_argument,	NULL, 'r'},
			{"realtime",	required_argument,	NULL, 'r'},
			{"system",	no_argument,		NULL, 's'},
			{"system",	no_argument,		NULL, 's'},
			{"inherit",	no_argument,		NULL, 'i'},
			{"inherit",	no_argument,		NULL, 'i'},
			{"nmi",		no_argument,		NULL, 'n'},
			{"nmi",		no_argument,		NULL, 'n'},
			{NULL,		0,			NULL,  0 }
			{NULL,		0,			NULL,  0 }
		};
		};
		int c = getopt_long(argc, argv, "+:c:e:m:o:r:sin",
		int c = getopt_long(argc, argv, "+:c:e:m:o:p:r:sin",
				    long_options, &option_index);
				    long_options, &option_index);
		if (c == -1)
		if (c == -1)
			break;
			break;
@@ -215,6 +221,7 @@ static void process_options(int argc, const char *argv[])
		case 'e': error				= parse_events(optarg); break;
		case 'e': error				= parse_events(optarg); break;
		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 'p': target_pid			=   atoi(optarg); break;
		case 'r': realtime_prio			=   atoi(optarg); break;
		case 'r': realtime_prio			=   atoi(optarg); break;
		case 's': system_wide                   ^=             1; break;
		case 's': system_wide                   ^=             1; break;
		case 'i': inherit			^=	       1; break;
		case 'i': inherit			^=	       1; break;
@@ -223,7 +230,7 @@ static void process_options(int argc, const char *argv[])
		}
		}
	}
	}


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


	if (error)
	if (error)
@@ -350,15 +357,135 @@ static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
static int nr_poll;
static int nr_poll;
static int nr_cpu;
static int nr_cpu;


static void open_counters(int cpu)
struct mmap_event {
	struct perf_event_header header;
	__u32 pid, tid;
	__u64 start;
	__u64 len;
	__u64 pgoff;
	char filename[PATH_MAX];
};
struct comm_event {
	struct perf_event_header header;
	__u32 pid,tid;
	char comm[16];
};

static pid_t pid_synthesize_comm_event(pid_t pid)
{
	char filename[PATH_MAX];
	char bf[BUFSIZ];
	struct comm_event comm_ev;
	size_t size;
	int fd;

	snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);

	fd = open(filename, O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "couldn't open %s\n", filename);
		exit(EXIT_FAILURE);
	}
	if (read(fd, bf, sizeof(bf)) < 0) {
		fprintf(stderr, "couldn't read %s\n", filename);
		exit(EXIT_FAILURE);
	}
	close(fd);

	pid_t spid, ppid;
	char state;
	char comm[18];

	memset(&comm_ev, 0, sizeof(comm_ev));
        int nr = sscanf(bf, "%d %s %c %d %d ",
			&spid, comm, &state, &ppid, &comm_ev.pid);
	if (nr != 5) {
		fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n",
			filename);
		exit(EXIT_FAILURE);
	}
	comm_ev.header.type = PERF_EVENT_COMM;
	comm_ev.tid = pid;
	size = strlen(comm);
	comm[--size] = '\0'; /* Remove the ')' at the end */
	--size; /* Remove the '(' at the begin */
	memcpy(comm_ev.comm, comm + 1, size);
	size = ALIGN(size, sizeof(uint64_t));
	comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
	int ret = write(output, &comm_ev, comm_ev.header.size);
	if (ret < 0) {
		perror("failed to write");
		exit(-1);
	}
	return comm_ev.pid;
}

static void pid_synthesize_mmap_events(pid_t pid, pid_t pgid)
{
	char filename[PATH_MAX];
	FILE *fp;

	snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);

	fp = fopen(filename, "r");
	if (fp == NULL) {
		fprintf(stderr, "couldn't open %s\n", filename);
		exit(EXIT_FAILURE);
	}
	while (1) {
		char bf[BUFSIZ];
		unsigned char vm_read, vm_write, vm_exec, vm_mayshare;
		struct mmap_event mmap_ev = {
			.header.type = PERF_EVENT_MMAP,
		};
		unsigned long ino;
		int major, minor;
		size_t size;
		if (fgets(bf, sizeof(bf), fp) == NULL)
			break;

		/* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
		sscanf(bf, "%llx-%llx %c%c%c%c %llx %x:%x %lu",
			&mmap_ev.start, &mmap_ev.len,
                        &vm_read, &vm_write, &vm_exec, &vm_mayshare,
                        &mmap_ev.pgoff, &major, &minor, &ino);
		if (vm_exec == 'x') {
			char *execname = strrchr(bf, ' ');

			if (execname == NULL || execname[1] != '/')
				continue;

			execname += 1;
			size = strlen(execname);
			execname[size - 1] = '\0'; /* Remove \n */
			memcpy(mmap_ev.filename, execname, size);
			size = ALIGN(size, sizeof(uint64_t));
			mmap_ev.len -= mmap_ev.start;
			mmap_ev.header.size = (sizeof(mmap_ev) -
					       (sizeof(mmap_ev.filename) - size));
			mmap_ev.pid = pgid;
			mmap_ev.tid = pid;

			if (write(output, &mmap_ev, mmap_ev.header.size) < 0) {
				perror("failed to write");
				exit(-1);
			}
		}
	}

	fclose(fp);
}

static void open_counters(int cpu, pid_t pid)
{
{
	struct perf_counter_hw_event hw_event;
	struct perf_counter_hw_event hw_event;
	int counter, group_fd;
	int counter, group_fd;
	int track = 1;
	int track = 1;
	pid_t pid = -1;


	if (cpu < 0)
	if (pid > 0) {
		pid = 0;
		pid_t pgid = pid_synthesize_comm_event(pid);
		pid_synthesize_mmap_events(pid, pgid);
	}


	group_fd = -1;
	group_fd = -1;
	for (counter = 0; counter < nr_counters; counter++) {
	for (counter = 0; counter < nr_counters; counter++) {
@@ -435,14 +562,15 @@ int cmd_record(int argc, const char **argv)
	argc -= optind;
	argc -= optind;
	argv += optind;
	argv += optind;


	if (!system_wide)
	if (!system_wide) {
		open_counters(-1);
		open_counters(-1, target_pid != -1 ? target_pid : 0);
	else for (i = 0; i < nr_cpus; i++)
	} else for (i = 0; i < nr_cpus; i++)
		open_counters(i);
		open_counters(i, target_pid);


	signal(SIGCHLD, sig_handler);
	signal(SIGCHLD, sig_handler);
	signal(SIGINT, sig_handler);
	signal(SIGINT, sig_handler);


	if (target_pid == -1) {
		pid = fork();
		pid = fork();
		if (pid < 0)
		if (pid < 0)
			perror("failed to fork");
			perror("failed to fork");
@@ -453,6 +581,7 @@ int cmd_record(int argc, const char **argv)
				exit(-1);
				exit(-1);
			}
			}
		}
		}
	}


	if (realtime_prio) {
	if (realtime_prio) {
		struct sched_param param;
		struct sched_param param;