tracing: Add kprobes event profiling interface
[pandora-kernel.git] / tools / perf / builtin-record.c
index d7ebbd7..0345aad 100644 (file)
@@ -14,6 +14,8 @@
 #include "util/parse-events.h"
 #include "util/string.h"
 
+#include "util/header.h"
+
 #include <unistd.h>
 #include <sched.h>
 
@@ -39,6 +41,9 @@ static int                    force                           = 0;
 static int                     append_file                     = 0;
 static int                     call_graph                      = 0;
 static int                     verbose                         = 0;
+static int                     inherit_stat                    = 0;
+static int                     no_samples                      = 0;
+static int                     sample_address                  = 0;
 
 static long                    samples;
 static struct timeval          last_read;
@@ -52,7 +57,8 @@ static int                    nr_poll;
 static int                     nr_cpu;
 
 static int                     file_new = 1;
-static struct perf_file_header file_header;
+
+struct perf_header             *header;
 
 struct mmap_event {
        struct perf_event_header        header;
@@ -289,7 +295,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)
        while (1) {
                char bf[BUFSIZ], *pbf = bf;
                struct mmap_event mmap_ev = {
-                       .header.type = PERF_EVENT_MMAP,
+                       .header = { .type = PERF_EVENT_MMAP },
                };
                int n;
                size_t size;
@@ -306,12 +312,15 @@ static void pid_synthesize_mmap_samples(pid_t pid)
                        continue;
                pbf += n + 3;
                if (*pbf == 'x') { /* vm_exec */
-                       char *execname = strrchr(bf, ' ');
+                       char *execname = strchr(bf, '/');
 
-                       if (execname == NULL || execname[1] != '/')
+                       /* Catch VDSO */
+                       if (execname == NULL)
+                               execname = strstr(bf, "[vdso]");
+
+                       if (execname == NULL)
                                continue;
 
-                       execname += 1;
                        size = strlen(execname);
                        execname[size - 1] = '\0'; /* Remove \n */
                        memcpy(mmap_ev.filename, execname, size);
@@ -329,7 +338,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)
        fclose(fp);
 }
 
-static void synthesize_samples(void)
+static void synthesize_all(void)
 {
        DIR *proc;
        struct dirent dirent, *next;
@@ -353,10 +362,35 @@ static void synthesize_samples(void)
 
 static int group_fd;
 
+static struct perf_header_attr *get_header_attr(struct perf_counter_attr *a, int nr)
+{
+       struct perf_header_attr *h_attr;
+
+       if (nr < header->attrs) {
+               h_attr = header->attr[nr];
+       } else {
+               h_attr = perf_header_attr__new(a);
+               perf_header__add_attr(header, h_attr);
+       }
+
+       return h_attr;
+}
+
 static void create_counter(int counter, int cpu, pid_t pid)
 {
        struct perf_counter_attr *attr = attrs + counter;
-       int track = 1;
+       struct perf_header_attr *h_attr;
+       int track = !counter; /* only the first counter needs these */
+       struct {
+               u64 count;
+               u64 time_enabled;
+               u64 time_running;
+               u64 id;
+       } read_data;
+
+       attr->read_format       = PERF_FORMAT_TOTAL_TIME_ENABLED |
+                                 PERF_FORMAT_TOTAL_TIME_RUNNING |
+                                 PERF_FORMAT_ID;
 
        attr->sample_type       = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
 
@@ -366,25 +400,24 @@ static void create_counter(int counter, int cpu, pid_t pid)
                attr->sample_freq       = freq;
        }
 
+       if (no_samples)
+               attr->sample_freq = 0;
+
+       if (inherit_stat)
+               attr->inherit_stat = 1;
+
+       if (sample_address)
+               attr->sample_type       |= PERF_SAMPLE_ADDR;
+
        if (call_graph)
                attr->sample_type       |= PERF_SAMPLE_CALLCHAIN;
 
-       if (file_new) {
-               file_header.sample_type = attr->sample_type;
-       } else {
-               if (file_header.sample_type != attr->sample_type) {
-                       fprintf(stderr, "incompatible append\n");
-                       exit(-1);
-               }
-       }
 
        attr->mmap              = track;
        attr->comm              = track;
        attr->inherit           = (cpu < 0) && inherit;
        attr->disabled          = 1;
 
-       track = 0; /* only the first counter needs these */
-
 try_again:
        fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0);
 
@@ -415,6 +448,22 @@ try_again:
                exit(-1);
        }
 
+       h_attr = get_header_attr(attr, counter);
+
+       if (!file_new) {
+               if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
+                       fprintf(stderr, "incompatible append\n");
+                       exit(-1);
+               }
+       }
+
+       if (read(fd[nr_cpu][counter], &read_data, sizeof(read_data)) == -1) {
+               perror("Unable to read perf file descriptor\n");
+               exit(-1);
+       }
+
+       perf_header_attr__add_id(h_attr, read_data.id);
+
        assert(fd[nr_cpu][counter] >= 0);
        fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);
 
@@ -445,11 +494,6 @@ static void open_counters(int cpu, pid_t pid)
 {
        int counter;
 
-       if (pid > 0) {
-               pid_synthesize_comm_event(pid, 0);
-               pid_synthesize_mmap_samples(pid);
-       }
-
        group_fd = -1;
        for (counter = 0; counter < nr_counters; counter++)
                create_counter(counter, cpu, pid);
@@ -459,17 +503,16 @@ static void open_counters(int cpu, pid_t pid)
 
 static void atexit_header(void)
 {
-       file_header.data_size += bytes_written;
+       header->data_size += bytes_written;
 
-       if (pwrite(output, &file_header, sizeof(file_header), 0) == -1)
-               perror("failed to write on file headers");
+       perf_header__write(header, output);
 }
 
 static int __cmd_record(int argc, const char **argv)
 {
        int i, counter;
        struct stat st;
-       pid_t pid;
+       pid_t pid = 0;
        int flags;
        int ret;
 
@@ -482,10 +525,14 @@ static int __cmd_record(int argc, const char **argv)
        signal(SIGCHLD, sig_handler);
        signal(SIGINT, sig_handler);
 
-       if (!stat(output_name, &st) && !force && !append_file) {
-               fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
-                               output_name);
-               exit(-1);
+       if (!stat(output_name, &st) && st.st_size) {
+               if (!force && !append_file) {
+                       fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
+                                       output_name);
+                       exit(-1);
+               }
+       } else {
+               append_file = 0;
        }
 
        flags = O_CREAT|O_RDWR;
@@ -500,22 +547,31 @@ static int __cmd_record(int argc, const char **argv)
                exit(-1);
        }
 
-       if (!file_new) {
-               if (read(output, &file_header, sizeof(file_header)) == -1) {
-                       perror("failed to read file headers");
-                       exit(-1);
-               }
-
-               lseek(output, file_header.data_size, SEEK_CUR);
-       }
+       if (!file_new)
+               header = perf_header__read(output);
+       else
+               header = perf_header__new();
 
        atexit(atexit_header);
 
        if (!system_wide) {
-               open_counters(-1, target_pid != -1 ? target_pid : getpid());
+               pid = target_pid;
+               if (pid == -1)
+                       pid = getpid();
+
+               open_counters(-1, pid);
        } else for (i = 0; i < nr_cpus; i++)
                open_counters(i, target_pid);
 
+       if (file_new)
+               perf_header__write(header, output);
+
+       if (!system_wide) {
+               pid_synthesize_comm_event(pid, 0);
+               pid_synthesize_mmap_samples(pid);
+       } else
+               synthesize_all();
+
        if (target_pid == -1 && argc) {
                pid = fork();
                if (pid < 0)
@@ -539,10 +595,7 @@ static int __cmd_record(int argc, const char **argv)
                }
        }
 
-       if (system_wide)
-               synthesize_samples();
-
-       while (!done) {
+       for (;;) {
                int hits = samples;
 
                for (i = 0; i < nr_cpu; i++) {
@@ -550,8 +603,11 @@ static int __cmd_record(int argc, const char **argv)
                                mmap_read(&mmap_array[i][counter]);
                }
 
-               if (hits == samples)
+               if (hits == samples) {
+                       if (done)
+                               break;
                        ret = poll(event_array, nr_poll, 100);
+               }
        }
 
        /*
@@ -600,14 +656,21 @@ static const struct option options[] = {
                    "do call-graph (stack chain/backtrace) recording"),
        OPT_BOOLEAN('v', "verbose", &verbose,
                    "be more verbose (show counter open errors, etc)"),
+       OPT_BOOLEAN('s', "stat", &inherit_stat,
+                   "per thread counts"),
+       OPT_BOOLEAN('d', "data", &sample_address,
+                   "Sample addresses"),
+       OPT_BOOLEAN('n', "no-samples", &no_samples,
+                   "don't sample"),
        OPT_END()
 };
 
-int cmd_record(int argc, const char **argv, const char *prefix)
+int cmd_record(int argc, const char **argv, const char *prefix __used)
 {
        int counter;
 
-       argc = parse_options(argc, argv, options, record_usage, 0);
+       argc = parse_options(argc, argv, options, record_usage,
+               PARSE_OPT_STOP_AT_NON_OPTION);
        if (!argc && target_pid == -1 && !system_wide)
                usage_with_options(record_usage, options);