* [PATCH] perf_counter: Allow specifying a pid to record
@ 2009-05-15 1:45 Arnaldo Carvalho de Melo
2009-05-15 1:50 ` [PATCH v2] " Arnaldo Carvalho de Melo
0 siblings, 1 reply; 4+ messages in thread
From: Arnaldo Carvalho de Melo @ 2009-05-15 1:45 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, Clark Williams, John Kacur,
Linux Kernel Mailing List
commit ee8ffeaf1ba6aee56cf822cba291fbacb5dd450b
Author: Arnaldo Carvalho de Melo <acme@redhat.com>
Date: Thu May 14 22:41:18 2009 -0300
perf_count: Allow connecting to an existing thread
Impact: new command line option
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 record' can get what
it needs.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
diff --git a/Documentation/perf_counter/builtin-record.c b/Documentation/perf_counter/builtin-record.c
index 5f5e6df..fa57f4b 100644
--- a/Documentation/perf_counter/builtin-record.c
+++ b/Documentation/perf_counter/builtin-record.c
@@ -34,6 +34,9 @@
#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 __u64 event_id[MAX_COUNTERS] = { };
static int default_interval = 100000;
@@ -47,6 +50,7 @@ static char *output_name = "output.perf";
static int group = 0;
static unsigned int realtime_prio = 0;
static int system_wide = 0;
+static pid_t target_pid = -1;
static int inherit = 1;
static int nmi = 1;
@@ -180,6 +184,7 @@ static void display_help(void)
" -c CNT --count=CNT # event period to sample\n"
" -m pages --mmap_pages=<pages> # number of mmap data pages\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"
" -s --system # system wide profiling\n"
);
@@ -199,13 +204,14 @@ static void process_options(int argc, const char *argv[])
{"event", required_argument, NULL, 'e'},
{"mmap_pages", required_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'},
+ {"pid", required_argument, NULL, 'p'},
{"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:sin",
+ int c = getopt_long(argc, argv, "+:c:e:m:o:p:r:sin",
long_options, &option_index);
if (c == -1)
break;
@@ -215,6 +221,7 @@ static void process_options(int argc, const char *argv[])
case 'e': error = parse_events(optarg); break;
case 'm': mmap_pages = atoi(optarg); break;
case 'o': output_name = strdup(optarg); break;
+ case 'p': target_pid = atoi(optarg); break;
case 'r': realtime_prio = atoi(optarg); break;
case 's': system_wide ^= 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;
if (error)
@@ -350,15 +357,135 @@ 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 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;
int counter, group_fd;
int track = 1;
- pid_t pid = -1;
- if (cpu < 0)
- pid = 0;
+ if (pid > 0) {
+ pid_t pgid = pid_synthesize_comm_event(pid);
+ pid_synthesize_mmap_events(pid, pgid);
+ }
group_fd = -1;
for (counter = 0; counter < nr_counters; counter++) {
@@ -435,22 +562,24 @@ int cmd_record(int argc, const char **argv)
argc -= optind;
argv += optind;
- if (!system_wide)
- open_counters(-1);
- else for (i = 0; i < nr_cpus; i++)
- open_counters(i);
+ if (!system_wide) {
+ open_counters(-1, target_pid != -1 ? target_pid : 0);
+ } else for (i = 0; i < nr_cpus; i++)
+ open_counters(i, target_pid);
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
- pid = fork();
- if (pid < 0)
- perror("failed to fork");
+ if (target_pid == -1) {
+ pid = fork();
+ if (pid < 0)
+ perror("failed to fork");
- if (!pid) {
- if (execvp(argv[0], argv)) {
- perror(argv[0]);
- exit(-1);
+ if (!pid) {
+ if (execvp(argv[0], argv)) {
+ perror(argv[0]);
+ exit(-1);
+ }
}
}
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2] perf_counter: Allow specifying a pid to record
2009-05-15 1:45 [PATCH] perf_counter: Allow specifying a pid to record Arnaldo Carvalho de Melo
@ 2009-05-15 1:50 ` Arnaldo Carvalho de Melo
2009-05-15 7:33 ` Ingo Molnar
2009-05-15 7:36 ` [tip:perfcounters/core] perf record: " tip-bot for Arnaldo Carvalho de Melo
0 siblings, 2 replies; 4+ messages in thread
From: Arnaldo Carvalho de Melo @ 2009-05-15 1:50 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, Clark Williams, John Kacur,
Linux Kernel Mailing List
Em Thu, May 14, 2009 at 10:45:27PM -0300, Arnaldo Carvalho de Melo escreveu:
> commit ee8ffeaf1ba6aee56cf822cba291fbacb5dd450b
> Author: Arnaldo Carvalho de Melo <acme@redhat.com>
> Date: Thu May 14 22:41:18 2009 -0300
>
> perf_count: Allow connecting to an existing thread
>
> Impact: new command line option
>
> 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 record' can get what
> it needs.
Grr, it should read "so that 'perf report' can get", new patch below.
commit 37216038fe2807ee0725e221675914cd41233541
Author: Arnaldo Carvalho de Melo <acme@redhat.com>
Date: Thu May 14 22:41:18 2009 -0300
perf_count: Allow connecting to an existing thread
Impact: new command line option
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.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
diff --git a/Documentation/perf_counter/builtin-record.c b/Documentation/perf_counter/builtin-record.c
index 5f5e6df..fa57f4b 100644
--- a/Documentation/perf_counter/builtin-record.c
+++ b/Documentation/perf_counter/builtin-record.c
@@ -34,6 +34,9 @@
#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 __u64 event_id[MAX_COUNTERS] = { };
static int default_interval = 100000;
@@ -47,6 +50,7 @@ static char *output_name = "output.perf";
static int group = 0;
static unsigned int realtime_prio = 0;
static int system_wide = 0;
+static pid_t target_pid = -1;
static int inherit = 1;
static int nmi = 1;
@@ -180,6 +184,7 @@ static void display_help(void)
" -c CNT --count=CNT # event period to sample\n"
" -m pages --mmap_pages=<pages> # number of mmap data pages\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"
" -s --system # system wide profiling\n"
);
@@ -199,13 +204,14 @@ static void process_options(int argc, const char *argv[])
{"event", required_argument, NULL, 'e'},
{"mmap_pages", required_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'},
+ {"pid", required_argument, NULL, 'p'},
{"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:sin",
+ int c = getopt_long(argc, argv, "+:c:e:m:o:p:r:sin",
long_options, &option_index);
if (c == -1)
break;
@@ -215,6 +221,7 @@ static void process_options(int argc, const char *argv[])
case 'e': error = parse_events(optarg); break;
case 'm': mmap_pages = atoi(optarg); break;
case 'o': output_name = strdup(optarg); break;
+ case 'p': target_pid = atoi(optarg); break;
case 'r': realtime_prio = atoi(optarg); break;
case 's': system_wide ^= 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;
if (error)
@@ -350,15 +357,135 @@ 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 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;
int counter, group_fd;
int track = 1;
- pid_t pid = -1;
- if (cpu < 0)
- pid = 0;
+ if (pid > 0) {
+ pid_t pgid = pid_synthesize_comm_event(pid);
+ pid_synthesize_mmap_events(pid, pgid);
+ }
group_fd = -1;
for (counter = 0; counter < nr_counters; counter++) {
@@ -435,22 +562,24 @@ int cmd_record(int argc, const char **argv)
argc -= optind;
argv += optind;
- if (!system_wide)
- open_counters(-1);
- else for (i = 0; i < nr_cpus; i++)
- open_counters(i);
+ if (!system_wide) {
+ open_counters(-1, target_pid != -1 ? target_pid : 0);
+ } else for (i = 0; i < nr_cpus; i++)
+ open_counters(i, target_pid);
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
- pid = fork();
- if (pid < 0)
- perror("failed to fork");
+ if (target_pid == -1) {
+ pid = fork();
+ if (pid < 0)
+ perror("failed to fork");
- if (!pid) {
- if (execvp(argv[0], argv)) {
- perror(argv[0]);
- exit(-1);
+ if (!pid) {
+ if (execvp(argv[0], argv)) {
+ perror(argv[0]);
+ exit(-1);
+ }
}
}
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2] perf_counter: Allow specifying a pid to record
2009-05-15 1:50 ` [PATCH v2] " Arnaldo Carvalho de Melo
@ 2009-05-15 7:33 ` Ingo Molnar
2009-05-15 7:36 ` [tip:perfcounters/core] perf record: " tip-bot for Arnaldo Carvalho de Melo
1 sibling, 0 replies; 4+ messages in thread
From: Ingo Molnar @ 2009-05-15 7:33 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, Clark Williams, John Kacur,
Linux Kernel Mailing List
* Arnaldo Carvalho de Melo <acme@redhat.com> wrote:
> Em Thu, May 14, 2009 at 10:45:27PM -0300, Arnaldo Carvalho de Melo escreveu:
> > commit ee8ffeaf1ba6aee56cf822cba291fbacb5dd450b
> > Author: Arnaldo Carvalho de Melo <acme@redhat.com>
> > Date: Thu May 14 22:41:18 2009 -0300
> >
> > perf_count: Allow connecting to an existing thread
> >
> > Impact: new command line option
> >
> > 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 record' can get what
> > it needs.
>
> Grr, it should read "so that 'perf report' can get", new patch below.
>
> commit 37216038fe2807ee0725e221675914cd41233541
> Author: Arnaldo Carvalho de Melo <acme@redhat.com>
> Date: Thu May 14 22:41:18 2009 -0300
>
> perf_count: Allow connecting to an existing thread
>
> Impact: new command line option
>
> 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.
>
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Very nice, applied it - thanks Arnaldo!
> +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);
> +}
Neat - this was one of the holes in the concept :)
Ingo
^ permalink raw reply [flat|nested] 4+ messages in thread
* [tip:perfcounters/core] perf record: Allow specifying a pid to record
2009-05-15 1:50 ` [PATCH v2] " Arnaldo Carvalho de Melo
2009-05-15 7:33 ` Ingo Molnar
@ 2009-05-15 7:36 ` tip-bot for Arnaldo Carvalho de Melo
1 sibling, 0 replies; 4+ messages in thread
From: tip-bot for Arnaldo Carvalho de Melo @ 2009-05-15 7:36 UTC (permalink / raw)
To: linux-tip-commits
Cc: linux-kernel, acme, hpa, mingo, jkacur, williams, peterz, tglx,
mingo
Commit-ID: 1a853e36871b533ccc3f3c5bdd5cd0d867043a00
Gitweb: http://git.kernel.org/tip/1a853e36871b533ccc3f3c5bdd5cd0d867043a00
Author: Arnaldo Carvalho de Melo <acme@redhat.com>
AuthorDate: Thu, 14 May 2009 22:50:46 -0300
Committer: Ingo Molnar <mingo@elte.hu>
CommitDate: Fri, 15 May 2009 09:35:24 +0200
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: Arnaldo 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: Ingo Molnar <mingo@elte.hu>
---
Documentation/perf_counter/builtin-record.c | 163 ++++++++++++++++++++++++---
1 files changed, 146 insertions(+), 17 deletions(-)
diff --git a/Documentation/perf_counter/builtin-record.c b/Documentation/perf_counter/builtin-record.c
index 5f5e6df..efb8759 100644
--- a/Documentation/perf_counter/builtin-record.c
+++ b/Documentation/perf_counter/builtin-record.c
@@ -34,6 +34,9 @@
#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 __u64 event_id[MAX_COUNTERS] = { };
static int default_interval = 100000;
@@ -47,6 +50,7 @@ static char *output_name = "output.perf";
static int group = 0;
static unsigned int realtime_prio = 0;
static int system_wide = 0;
+static pid_t target_pid = -1;
static int inherit = 1;
static int nmi = 1;
@@ -180,6 +184,7 @@ static void display_help(void)
" -c CNT --count=CNT # event period to sample\n"
" -m pages --mmap_pages=<pages> # number of mmap data pages\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"
" -s --system # system wide profiling\n"
);
@@ -199,13 +204,14 @@ static void process_options(int argc, const char *argv[])
{"event", required_argument, NULL, 'e'},
{"mmap_pages", required_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'},
+ {"pid", required_argument, NULL, 'p'},
{"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:sin",
+ int c = getopt_long(argc, argv, "+:c:e:m:o:p:r:sin",
long_options, &option_index);
if (c == -1)
break;
@@ -215,6 +221,7 @@ static void process_options(int argc, const char *argv[])
case 'e': error = parse_events(optarg); break;
case 'm': mmap_pages = atoi(optarg); break;
case 'o': output_name = strdup(optarg); break;
+ case 'p': target_pid = atoi(optarg); break;
case 'r': realtime_prio = atoi(optarg); break;
case 's': system_wide ^= 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;
if (error)
@@ -350,15 +357,135 @@ 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 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;
int counter, group_fd;
int track = 1;
- pid_t pid = -1;
- if (cpu < 0)
- pid = 0;
+ if (pid > 0) {
+ pid_t pgid = pid_synthesize_comm_event(pid);
+ pid_synthesize_mmap_events(pid, pgid);
+ }
group_fd = -1;
for (counter = 0; counter < nr_counters; counter++) {
@@ -435,22 +562,24 @@ int cmd_record(int argc, const char **argv)
argc -= optind;
argv += optind;
- if (!system_wide)
- open_counters(-1);
- else for (i = 0; i < nr_cpus; i++)
- open_counters(i);
+ if (!system_wide) {
+ open_counters(-1, target_pid != -1 ? target_pid : 0);
+ } else for (i = 0; i < nr_cpus; i++)
+ open_counters(i, target_pid);
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
- pid = fork();
- if (pid < 0)
- perror("failed to fork");
+ if (target_pid == -1) {
+ pid = fork();
+ if (pid < 0)
+ perror("failed to fork");
- if (!pid) {
- if (execvp(argv[0], argv)) {
- perror(argv[0]);
- exit(-1);
+ if (!pid) {
+ if (execvp(argv[0], argv)) {
+ perror(argv[0]);
+ exit(-1);
+ }
}
}
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2009-05-15 7:37 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-05-15 1:45 [PATCH] perf_counter: Allow specifying a pid to record Arnaldo Carvalho de Melo
2009-05-15 1:50 ` [PATCH v2] " Arnaldo Carvalho de Melo
2009-05-15 7:33 ` Ingo Molnar
2009-05-15 7:36 ` [tip:perfcounters/core] perf record: " tip-bot for Arnaldo Carvalho de Melo
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.