perf_counter tools: Add a data file header
[safe/jmp/linux-2.6] / tools / perf / builtin-record.c
index aeab9c4..2830467 100644 (file)
@@ -37,6 +37,8 @@ static pid_t                  target_pid                      = -1;
 static int                     inherit                         = 1;
 static int                     force                           = 0;
 static int                     append_file                     = 0;
+static int                     call_graph                      = 0;
+static int                     verbose                         = 0;
 
 static long                    samples;
 static struct timeval          last_read;
@@ -49,6 +51,9 @@ static struct pollfd          event_array[MAX_NR_CPUS * MAX_COUNTERS];
 static int                     nr_poll;
 static int                     nr_cpu;
 
+static int                     file_new = 1;
+static struct perf_file_header file_header;
+
 struct mmap_event {
        struct perf_event_header        header;
        __u32                           pid;
@@ -76,10 +81,10 @@ struct mmap_data {
 
 static struct mmap_data                mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
 
-static unsigned int mmap_read_head(struct mmap_data *md)
+static unsigned long mmap_read_head(struct mmap_data *md)
 {
        struct perf_counter_mmap_page *pc = md->base;
-       int head;
+       long head;
 
        head = pc->data_head;
        rmb();
@@ -87,6 +92,32 @@ static unsigned int mmap_read_head(struct mmap_data *md)
        return head;
 }
 
+static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
+{
+       struct perf_counter_mmap_page *pc = md->base;
+
+       /*
+        * ensure all reads are done before we write the tail out.
+        */
+       /* mb(); */
+       pc->data_tail = tail;
+}
+
+static void write_output(void *buf, size_t size)
+{
+       while (size) {
+               int ret = write(output, buf, size);
+
+               if (ret < 0)
+                       die("failed to write");
+
+               size -= ret;
+               buf += ret;
+
+               bytes_written += ret;
+       }
+}
+
 static void mmap_read(struct mmap_data *md)
 {
        unsigned int head = mmap_read_head(md);
@@ -107,7 +138,7 @@ static void mmap_read(struct mmap_data *md)
         * In either case, truncate and restart at head.
         */
        diff = head - old;
-       if (diff > md->mask / 2 || diff < 0) {
+       if (diff < 0) {
                struct timeval iv;
                unsigned long msecs;
 
@@ -135,43 +166,35 @@ static void mmap_read(struct mmap_data *md)
                size = md->mask + 1 - (old & md->mask);
                old += size;
 
-               while (size) {
-                       int ret = write(output, buf, size);
-
-                       if (ret < 0)
-                               die("failed to write");
-
-                       size -= ret;
-                       buf += ret;
-
-                       bytes_written += ret;
-               }
+               write_output(buf, size);
        }
 
        buf = &data[old & md->mask];
        size = head - old;
        old += size;
 
-       while (size) {
-               int ret = write(output, buf, size);
-
-               if (ret < 0)
-                       die("failed to write");
-
-               size -= ret;
-               buf += ret;
-
-               bytes_written += ret;
-       }
+       write_output(buf, size);
 
        md->prev = old;
+       mmap_write_tail(md, old);
 }
 
 static volatile int done = 0;
+static volatile int signr = -1;
 
 static void sig_handler(int sig)
 {
        done = 1;
+       signr = sig;
+}
+
+static void sig_atexit(void)
+{
+       if (signr == -1)
+               return;
+
+       signal(signr, SIG_DFL);
+       kill(getpid(), signr);
 }
 
 static void pid_synthesize_comm_event(pid_t pid, int full)
@@ -179,7 +202,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
        struct comm_event comm_ev;
        char filename[PATH_MAX];
        char bf[BUFSIZ];
-       int fd, ret;
+       int fd;
        size_t size;
        char *field, *sep;
        DIR *tasks;
@@ -189,8 +212,12 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
 
        fd = open(filename, O_RDONLY);
        if (fd < 0) {
-               fprintf(stderr, "couldn't open %s\n", filename);
-               exit(EXIT_FAILURE);
+               /*
+                * We raced with a task exiting - just return:
+                */
+               if (verbose)
+                       fprintf(stderr, "couldn't open %s\n", filename);
+               return;
        }
        if (read(fd, bf, sizeof(bf)) < 0) {
                fprintf(stderr, "couldn't read %s\n", filename);
@@ -211,17 +238,13 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
 
        comm_ev.pid = pid;
        comm_ev.header.type = PERF_EVENT_COMM;
-       size = ALIGN(size, sizeof(uint64_t));
+       size = ALIGN(size, sizeof(__u64));
        comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
 
        if (!full) {
                comm_ev.tid = pid;
 
-               ret = write(output, &comm_ev, comm_ev.header.size);
-               if (ret < 0) {
-                       perror("failed to write");
-                       exit(-1);
-               }
+               write_output(&comm_ev, comm_ev.header.size);
                return;
        }
 
@@ -236,11 +259,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
 
                comm_ev.tid = pid;
 
-               ret = write(output, &comm_ev, comm_ev.header.size);
-               if (ret < 0) {
-                       perror("failed to write");
-                       exit(-1);
-               }
+               write_output(&comm_ev, comm_ev.header.size);
        }
        closedir(tasks);
        return;
@@ -260,8 +279,12 @@ static void pid_synthesize_mmap_samples(pid_t pid)
 
        fp = fopen(filename, "r");
        if (fp == NULL) {
-               fprintf(stderr, "couldn't open %s\n", filename);
-               exit(EXIT_FAILURE);
+               /*
+                * We raced with a task exiting - just return:
+                */
+               if (verbose)
+                       fprintf(stderr, "couldn't open %s\n", filename);
+               return;
        }
        while (1) {
                char bf[BUFSIZ], *pbf = bf;
@@ -292,17 +315,14 @@ static void pid_synthesize_mmap_samples(pid_t pid)
                        size = strlen(execname);
                        execname[size - 1] = '\0'; /* Remove \n */
                        memcpy(mmap_ev.filename, execname, size);
-                       size = ALIGN(size, sizeof(uint64_t));
+                       size = ALIGN(size, sizeof(__u64));
                        mmap_ev.len -= mmap_ev.start;
                        mmap_ev.header.size = (sizeof(mmap_ev) -
                                               (sizeof(mmap_ev.filename) - size));
                        mmap_ev.pid = pid;
                        mmap_ev.tid = pid;
 
-                       if (write(output, &mmap_ev, mmap_ev.header.size) < 0) {
-                               perror("failed to write");
-                               exit(-1);
-                       }
+                       write_output(&mmap_ev, mmap_ev.header.size);
                }
        }
 
@@ -338,28 +358,63 @@ static void create_counter(int counter, int cpu, pid_t pid)
        struct perf_counter_attr *attr = attrs + counter;
        int track = 1;
 
-       attr->sample_type       = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_PERIOD;
+       attr->sample_type       = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
+
        if (freq) {
+               attr->sample_type       |= PERF_SAMPLE_PERIOD;
                attr->freq              = 1;
                attr->sample_freq       = freq;
        }
+
+       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);
 
        if (fd[nr_cpu][counter] < 0) {
                int err = errno;
 
-               error("syscall returned with %d (%s)\n",
-                               fd[nr_cpu][counter], strerror(err));
                if (err == EPERM)
-                       printf("Are you root?\n");
+                       die("Permission error - are you root?\n");
+
+               /*
+                * If it's cycles then fall back to hrtimer
+                * based cpu-clock-tick sw counter, which
+                * is always available even if no PMU support:
+                */
+               if (attr->type == PERF_TYPE_HARDWARE
+                       && attr->config == PERF_COUNT_HW_CPU_CYCLES) {
+
+                       if (verbose)
+                               warning(" ... trying to fall back to cpu-clock-ticks\n");
+                       attr->type = PERF_TYPE_SOFTWARE;
+                       attr->config = PERF_COUNT_SW_CPU_CLOCK;
+                       goto try_again;
+               }
+               printf("\n");
+               error("perfcounter syscall returned with %d (%s)\n",
+                       fd[nr_cpu][counter], strerror(err));
+               die("No CONFIG_PERF_COUNTERS=y kernel support configured?\n");
                exit(-1);
        }
+
        assert(fd[nr_cpu][counter] >= 0);
        fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);
 
@@ -377,11 +432,13 @@ static void create_counter(int counter, int cpu, pid_t pid)
        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);
+                       PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0);
        if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {
                error("failed to mmap with %d (%s)\n", errno, strerror(errno));
                exit(-1);
        }
+
+       ioctl(fd[nr_cpu][counter], PERF_COUNTER_IOC_ENABLE);
 }
 
 static void open_counters(int cpu, pid_t pid)
@@ -400,6 +457,13 @@ static void open_counters(int cpu, pid_t pid)
        nr_cpu++;
 }
 
+static void atexit_header(void)
+{
+       file_header.data_size += bytes_written;
+
+       pwrite(output, &file_header, sizeof(file_header), 0);
+}
+
 static int __cmd_record(int argc, const char **argv)
 {
        int i, counter;
@@ -413,6 +477,10 @@ static int __cmd_record(int argc, const char **argv)
        assert(nr_cpus <= MAX_NR_CPUS);
        assert(nr_cpus >= 0);
 
+       atexit(sig_atexit);
+       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);
@@ -421,7 +489,7 @@ static int __cmd_record(int argc, const char **argv)
 
        flags = O_CREAT|O_RDWR;
        if (append_file)
-               flags |= O_APPEND;
+               file_new = 0;
        else
                flags |= O_TRUNC;
 
@@ -431,14 +499,18 @@ static int __cmd_record(int argc, const char **argv)
                exit(-1);
        }
 
+       if (!file_new) {
+               read(output, &file_header, sizeof(file_header));
+               lseek(output, file_header.data_size, SEEK_CUR);
+       }
+
+       atexit(atexit_header);
+
        if (!system_wide) {
                open_counters(-1, target_pid != -1 ? target_pid : getpid());
        } else for (i = 0; i < nr_cpus; i++)
                open_counters(i, target_pid);
 
-       signal(SIGCHLD, sig_handler);
-       signal(SIGINT, sig_handler);
-
        if (target_pid == -1 && argc) {
                pid = fork();
                if (pid < 0)
@@ -519,6 +591,10 @@ static const struct option options[] = {
                    "profile at this frequency"),
        OPT_INTEGER('m', "mmap-pages", &mmap_pages,
                    "number of mmap data pages"),
+       OPT_BOOLEAN('g', "call-graph", &call_graph,
+                   "do call-graph (stack chain/backtrace) recording"),
+       OPT_BOOLEAN('v', "verbose", &verbose,
+                   "be more verbose (show counter open errors, etc)"),
        OPT_END()
 };
 
@@ -530,8 +606,11 @@ int cmd_record(int argc, const char **argv, const char *prefix)
        if (!argc && target_pid == -1 && !system_wide)
                usage_with_options(record_usage, options);
 
-       if (!nr_counters)
-               nr_counters = 1;
+       if (!nr_counters) {
+               nr_counters     = 1;
+               attrs[0].type   = PERF_TYPE_HARDWARE;
+               attrs[0].config = PERF_COUNT_HW_CPU_CYCLES;
+       }
 
        for (counter = 0; counter < nr_counters; counter++) {
                if (attrs[counter].sample_period)