perf session: Create kernel maps in the constructor
[safe/jmp/linux-2.6] / tools / perf / builtin-record.c
index ff93f8e..9034522 100644 (file)
 #include "util/header.h"
 #include "util/event.h"
 #include "util/debug.h"
-#include "util/trace-event.h"
+#include "util/session.h"
+#include "util/symbol.h"
 
 #include <unistd.h>
 #include <sched.h>
 
-#define ALIGN(x, a)            __ALIGN_MASK(x, (typeof(x))(a)-1)
-#define __ALIGN_MASK(x, mask)  (((x)+(mask))&~(mask))
-
 static int                     fd[MAX_NR_CPUS][MAX_COUNTERS];
 
-static long                    default_interval                = 100000;
+static long                    default_interval                =      0;
 
-static int                     nr_cpus                         = 0;
+static int                     nr_cpus                         =      0;
 static unsigned int            page_size;
-static unsigned int            mmap_pages                      = 128;
-static int                     freq                            = 0;
+static unsigned int            mmap_pages                      =    128;
+static int                     freq                            =   1000;
 static int                     output;
 static const char              *output_name                    = "perf.data";
-static int                     group                           = 0;
-static unsigned int            realtime_prio                   = 0;
-static int                     raw_samples                     = 0;
-static int                     system_wide                     = 0;
-static int                     profile_cpu                     = -1;
-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                     inherit_stat                    = 0;
-static int                     no_samples                      = 0;
-static int                     sample_address                  = 0;
-
-static long                    samples;
+static int                     group                           =      0;
+static unsigned int            realtime_prio                   =      0;
+static int                     raw_samples                     =      0;
+static int                     system_wide                     =      0;
+static int                     profile_cpu                     =     -1;
+static pid_t                   target_pid                      =     -1;
+static pid_t                   child_pid                       =     -1;
+static int                     inherit                         =      1;
+static int                     force                           =      0;
+static int                     append_file                     =      0;
+static int                     call_graph                      =      0;
+static int                     inherit_stat                    =      0;
+static int                     no_samples                      =      0;
+static int                     sample_address                  =      0;
+static int                     multiplex                       =      0;
+static int                     multiplex_fd                    =     -1;
+
+static long                    samples                         =      0;
 static struct timeval          last_read;
 static struct timeval          this_read;
 
-static u64                     bytes_written;
+static u64                     bytes_written                   =      0;
 
 static struct pollfd           event_array[MAX_NR_CPUS * MAX_COUNTERS];
 
-static int                     nr_poll;
-static int                     nr_cpu;
+static int                     nr_poll                         =      0;
+static int                     nr_cpu                          =      0;
 
-static int                     file_new = 1;
+static int                     file_new                        =      1;
 
-struct perf_header             *header;
+static struct perf_session     *session;
 
 struct mmap_data {
        int                     counter;
@@ -75,7 +76,7 @@ static struct mmap_data               mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
 
 static unsigned long mmap_read_head(struct mmap_data *md)
 {
-       struct perf_counter_mmap_page *pc = md->base;
+       struct perf_event_mmap_page *pc = md->base;
        long head;
 
        head = pc->data_head;
@@ -86,7 +87,7 @@ static unsigned long mmap_read_head(struct mmap_data *md)
 
 static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
 {
-       struct perf_counter_mmap_page *pc = md->base;
+       struct perf_event_mmap_page *pc = md->base;
 
        /*
         * ensure all reads are done before we write the tail out.
@@ -110,6 +111,37 @@ static void write_output(void *buf, size_t size)
        }
 }
 
+static void write_event(event_t *buf, size_t size)
+{
+       size_t processed_size = buf->header.size;
+       event_t *ev = buf;
+
+       do {
+               /*
+               * Add it to the list of DSOs, so that when we finish this
+                * record session we can pick the available build-ids.
+                */
+               if (ev->header.type == PERF_RECORD_MMAP) {
+                       struct list_head *head = &dsos__user;
+                       if (ev->header.misc == 1)
+                               head = &dsos__kernel;
+                       __dsos__findnew(head, ev->mmap.filename);
+               }
+
+               ev = ((void *)ev) + ev->header.size;
+               processed_size += ev->header.size;
+       } while (processed_size < size);
+
+       write_output(buf, size);
+}
+
+static int process_synthesized_event(event_t *event,
+                                    struct perf_session *self __used)
+{
+       write_event(event, event->header.size);
+       return 0;
+}
+
 static void mmap_read(struct mmap_data *md)
 {
        unsigned int head = mmap_read_head(md);
@@ -158,14 +190,14 @@ static void mmap_read(struct mmap_data *md)
                size = md->mask + 1 - (old & md->mask);
                old += size;
 
-               write_output(buf, size);
+               write_event(buf, size);
        }
 
        buf = &data[old & md->mask];
        size = head - old;
        old += size;
 
-       write_output(buf, size);
+       write_event(buf, size);
 
        md->prev = old;
        mmap_write_tail(md, old);
@@ -182,6 +214,9 @@ static void sig_handler(int sig)
 
 static void sig_atexit(void)
 {
+       if (child_pid != -1)
+               kill(child_pid, SIGTERM);
+
        if (signr == -1)
                return;
 
@@ -189,179 +224,21 @@ static void sig_atexit(void)
        kill(getpid(), signr);
 }
 
-static pid_t pid_synthesize_comm_event(pid_t pid, int full)
-{
-       struct comm_event comm_ev;
-       char filename[PATH_MAX];
-       char bf[BUFSIZ];
-       FILE *fp;
-       size_t size = 0;
-       DIR *tasks;
-       struct dirent dirent, *next;
-       pid_t tgid = 0;
-
-       snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
-
-       fp = fopen(filename, "r");
-       if (fp == NULL) {
-               /*
-                * We raced with a task exiting - just return:
-                */
-               if (verbose)
-                       fprintf(stderr, "couldn't open %s\n", filename);
-               return 0;
-       }
-
-       memset(&comm_ev, 0, sizeof(comm_ev));
-       while (!comm_ev.comm[0] || !comm_ev.pid) {
-               if (fgets(bf, sizeof(bf), fp) == NULL)
-                       goto out_failure;
-
-               if (memcmp(bf, "Name:", 5) == 0) {
-                       char *name = bf + 5;
-                       while (*name && isspace(*name))
-                               ++name;
-                       size = strlen(name) - 1;
-                       memcpy(comm_ev.comm, name, size++);
-               } else if (memcmp(bf, "Tgid:", 5) == 0) {
-                       char *tgids = bf + 5;
-                       while (*tgids && isspace(*tgids))
-                               ++tgids;
-                       tgid = comm_ev.pid = atoi(tgids);
-               }
-       }
-
-       comm_ev.header.type = PERF_EVENT_COMM;
-       size = ALIGN(size, sizeof(u64));
-       comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
-
-       if (!full) {
-               comm_ev.tid = pid;
-
-               write_output(&comm_ev, comm_ev.header.size);
-               goto out_fclose;
-       }
-
-       snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
-
-       tasks = opendir(filename);
-       while (!readdir_r(tasks, &dirent, &next) && next) {
-               char *end;
-               pid = strtol(dirent.d_name, &end, 10);
-               if (*end)
-                       continue;
-
-               comm_ev.tid = pid;
-
-               write_output(&comm_ev, comm_ev.header.size);
-       }
-       closedir(tasks);
-
-out_fclose:
-       fclose(fp);
-       return tgid;
-
-out_failure:
-       fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n",
-               filename);
-       exit(EXIT_FAILURE);
-}
-
-static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid)
-{
-       char filename[PATH_MAX];
-       FILE *fp;
-
-       snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
-
-       fp = fopen(filename, "r");
-       if (fp == NULL) {
-               /*
-                * 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;
-               struct mmap_event mmap_ev = {
-                       .header = { .type = PERF_EVENT_MMAP },
-               };
-               int n;
-               size_t size;
-               if (fgets(bf, sizeof(bf), fp) == NULL)
-                       break;
-
-               /* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
-               n = hex2u64(pbf, &mmap_ev.start);
-               if (n < 0)
-                       continue;
-               pbf += n + 1;
-               n = hex2u64(pbf, &mmap_ev.len);
-               if (n < 0)
-                       continue;
-               pbf += n + 3;
-               if (*pbf == 'x') { /* vm_exec */
-                       char *execname = strchr(bf, '/');
-
-                       /* Catch VDSO */
-                       if (execname == NULL)
-                               execname = strstr(bf, "[vdso]");
-
-                       if (execname == NULL)
-                               continue;
-
-                       size = strlen(execname);
-                       execname[size - 1] = '\0'; /* Remove \n */
-                       memcpy(mmap_ev.filename, execname, size);
-                       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 = tgid;
-                       mmap_ev.tid = pid;
-
-                       write_output(&mmap_ev, mmap_ev.header.size);
-               }
-       }
-
-       fclose(fp);
-}
-
-static void synthesize_all(void)
-{
-       DIR *proc;
-       struct dirent dirent, *next;
-
-       proc = opendir("/proc");
-
-       while (!readdir_r(proc, &dirent, &next) && next) {
-               char *end;
-               pid_t pid, tgid;
-
-               pid = strtol(dirent.d_name, &end, 10);
-               if (*end) /* only interested in proper numerical dirents */
-                       continue;
-
-               tgid = pid_synthesize_comm_event(pid, 1);
-               pid_synthesize_mmap_samples(pid, tgid);
-       }
-
-       closedir(proc);
-}
-
 static int group_fd;
 
-static struct perf_header_attr *get_header_attr(struct perf_counter_attr *a, int nr)
+static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)
 {
        struct perf_header_attr *h_attr;
 
-       if (nr < header->attrs) {
-               h_attr = header->attr[nr];
+       if (nr < session->header.attrs) {
+               h_attr = session->header.attr[nr];
        } else {
                h_attr = perf_header_attr__new(a);
-               perf_header__add_attr(header, h_attr);
+               if (h_attr != NULL)
+                       if (perf_header__add_attr(&session->header, h_attr) < 0) {
+                               perf_header_attr__delete(h_attr);
+                               h_attr = NULL;
+                       }
        }
 
        return h_attr;
@@ -369,9 +246,11 @@ static struct perf_header_attr *get_header_attr(struct perf_counter_attr *a, int
 
 static void create_counter(int counter, int cpu, pid_t pid)
 {
-       struct perf_counter_attr *attr = attrs + counter;
+       char *filter = filters[counter];
+       struct perf_event_attr *attr = attrs + counter;
        struct perf_header_attr *h_attr;
        int track = !counter; /* only the first counter needs these */
+       int ret;
        struct {
                u64 count;
                u64 time_enabled;
@@ -404,22 +283,23 @@ static void create_counter(int counter, int cpu, pid_t pid)
                attr->sample_type       |= PERF_SAMPLE_CALLCHAIN;
 
        if (raw_samples) {
+               attr->sample_type       |= PERF_SAMPLE_TIME;
                attr->sample_type       |= PERF_SAMPLE_RAW;
                attr->sample_type       |= PERF_SAMPLE_CPU;
        }
 
        attr->mmap              = track;
        attr->comm              = track;
-       attr->inherit           = (cpu < 0) && inherit;
+       attr->inherit           = inherit;
        attr->disabled          = 1;
 
 try_again:
-       fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0);
+       fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0);
 
        if (fd[nr_cpu][counter] < 0) {
                int err = errno;
 
-               if (err == EPERM)
+               if (err == EPERM || err == EACCES)
                        die("Permission error - are you root?\n");
                else if (err ==  ENODEV && profile_cpu != -1)
                        die("No such device - did you specify an out-of-range profile CPU?\n");
@@ -441,11 +321,19 @@ 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");
+
+#if defined(__i386__) || defined(__x86_64__)
+               if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP)
+                       die("No hardware sampling interrupt available. No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it.\n");
+#endif
+
+               die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
                exit(-1);
        }
 
        h_attr = get_header_attr(attr, counter);
+       if (h_attr == NULL)
+               die("nomem\n");
 
        if (!file_new) {
                if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
@@ -459,7 +347,10 @@ try_again:
                exit(-1);
        }
 
-       perf_header_attr__add_id(h_attr, read_data.id);
+       if (perf_header_attr__add_id(h_attr, read_data.id) < 0) {
+               pr_warning("Not enough memory to add id\n");
+               exit(-1);
+       }
 
        assert(fd[nr_cpu][counter] >= 0);
        fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);
@@ -469,22 +360,40 @@ try_again:
         */
        if (group && group_fd == -1)
                group_fd = fd[nr_cpu][counter];
+       if (multiplex && multiplex_fd == -1)
+               multiplex_fd = fd[nr_cpu][counter];
 
-       event_array[nr_poll].fd = fd[nr_cpu][counter];
-       event_array[nr_poll].events = POLLIN;
-       nr_poll++;
-
-       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|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);
+       if (multiplex && fd[nr_cpu][counter] != multiplex_fd) {
+
+               ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd);
+               assert(ret != -1);
+       } else {
+               event_array[nr_poll].fd = fd[nr_cpu][counter];
+               event_array[nr_poll].events = POLLIN;
+               nr_poll++;
+
+               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|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);
+       if (filter != NULL) {
+               ret = ioctl(fd[nr_cpu][counter],
+                           PERF_EVENT_IOC_SET_FILTER, filter);
+               if (ret) {
+                       error("failed to set filter with %d (%s)\n", errno,
+                             strerror(errno));
+                       exit(-1);
+               }
+       }
+
+       ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE);
 }
 
 static void open_counters(int cpu, pid_t pid)
@@ -500,9 +409,9 @@ static void open_counters(int cpu, pid_t pid)
 
 static void atexit_header(void)
 {
-       header->data_size += bytes_written;
+       session->header.data_size += bytes_written;
 
-       perf_header__write(header, output);
+       perf_header__write(&session->header, output, true);
 }
 
 static int __cmd_record(int argc, const char **argv)
@@ -511,7 +420,11 @@ static int __cmd_record(int argc, const char **argv)
        struct stat st;
        pid_t pid = 0;
        int flags;
-       int ret;
+       int err;
+       unsigned long waking = 0;
+       int child_ready_pipe[2], go_pipe[2];
+       const bool forks = target_pid == -1 && argc > 0;
+       char buf;
 
        page_size = sysconf(_SC_PAGE_SIZE);
        nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
@@ -522,11 +435,25 @@ static int __cmd_record(int argc, const char **argv)
        signal(SIGCHLD, sig_handler);
        signal(SIGINT, sig_handler);
 
+       if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
+               perror("failed to create pipes");
+               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);
+               if (!force) {
+                       if (!append_file) {
+                               pr_err("Error, output file %s exists, use -A "
+                                      "to append or -f to overwrite.\n",
+                                      output_name);
+                               exit(-1);
+                       }
+               } else {
+                       char oldname[PATH_MAX];
+                       snprintf(oldname, sizeof(oldname), "%s.old",
+                                output_name);
+                       unlink(oldname);
+                       rename(output_name, oldname);
                }
        } else {
                append_file = 0;
@@ -544,86 +471,160 @@ static int __cmd_record(int argc, const char **argv)
                exit(-1);
        }
 
-       if (!file_new)
-               header = perf_header__read(output);
-       else
-               header = perf_header__new();
+       session = perf_session__new(output_name, O_WRONLY, force);
+       if (session == NULL) {
+               pr_err("Not enough memory for reading perf file header\n");
+               return -1;
+       }
 
+       if (!file_new) {
+               err = perf_header__read(&session->header, output);
+               if (err < 0)
+                       return err;
+       }
 
        if (raw_samples) {
-               read_tracing_data(attrs, nr_counters);
+               perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
        } else {
                for (i = 0; i < nr_counters; i++) {
                        if (attrs[i].sample_type & PERF_SAMPLE_RAW) {
-                               read_tracing_data(attrs, nr_counters);
+                               perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
                                break;
                        }
                }
        }
+
        atexit(atexit_header);
 
-       if (!system_wide) {
-               pid = target_pid;
-               if (pid == -1)
-                       pid = getpid();
+       if (forks) {
+               pid = fork();
+               if (pid < 0) {
+                       perror("failed to fork");
+                       exit(-1);
+               }
 
-               open_counters(profile_cpu, pid);
-       } else {
-               if (profile_cpu != -1) {
-                       open_counters(profile_cpu, target_pid);
-               } else {
-                       for (i = 0; i < nr_cpus; i++)
-                               open_counters(i, target_pid);
+               if (!pid) {
+                       close(child_ready_pipe[0]);
+                       close(go_pipe[1]);
+                       fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
+
+                       /*
+                        * Do a dummy execvp to get the PLT entry resolved,
+                        * so we avoid the resolver overhead on the real
+                        * execvp call.
+                        */
+                       execvp("", (char **)argv);
+
+                       /*
+                        * Tell the parent we're ready to go
+                        */
+                       close(child_ready_pipe[1]);
+
+                       /*
+                        * Wait until the parent tells us to go.
+                        */
+                       if (read(go_pipe[0], &buf, 1) == -1)
+                               perror("unable to read pipe");
+
+                       execvp(argv[0], (char **)argv);
+
+                       perror(argv[0]);
+                       exit(-1);
+               }
+
+               child_pid = pid;
+
+               if (!system_wide)
+                       target_pid = pid;
+
+               close(child_ready_pipe[1]);
+               close(go_pipe[0]);
+               /*
+                * wait for child to settle
+                */
+               if (read(child_ready_pipe[0], &buf, 1) == -1) {
+                       perror("unable to read pipe");
+                       exit(-1);
                }
+               close(child_ready_pipe[0]);
        }
 
-       if (file_new)
-               perf_header__write(header, output);
 
-       if (!system_wide) {
-               pid_t tgid = pid_synthesize_comm_event(pid, 0);
-               pid_synthesize_mmap_samples(pid, tgid);
-       } else
-               synthesize_all();
+       if ((!system_wide && !inherit) || profile_cpu != -1) {
+               open_counters(profile_cpu, target_pid);
+       } else {
+               for (i = 0; i < nr_cpus; i++)
+                       open_counters(i, target_pid);
+       }
 
-       if (target_pid == -1 && argc) {
-               pid = fork();
-               if (pid < 0)
-                       perror("failed to fork");
+       if (file_new) {
+               err = perf_header__write(&session->header, output, false);
+               if (err < 0)
+                       return err;
+       }
 
-               if (!pid) {
-                       if (execvp(argv[0], (char **)argv)) {
-                               perror(argv[0]);
-                               exit(-1);
-                       }
-               }
+       err = event__synthesize_kernel_mmap(process_synthesized_event,
+                                           session, "_text");
+       if (err < 0) {
+               pr_err("Couldn't record kernel reference relocation symbol.\n");
+               return err;
+       }
+
+       err = event__synthesize_modules(process_synthesized_event, session);
+       if (err < 0) {
+               pr_err("Couldn't record kernel reference relocation symbol.\n");
+               return err;
        }
 
+       if (!system_wide && profile_cpu == -1)
+               event__synthesize_thread(pid, process_synthesized_event,
+                                        session);
+       else
+               event__synthesize_threads(process_synthesized_event, session);
+
        if (realtime_prio) {
                struct sched_param param;
 
                param.sched_priority = realtime_prio;
                if (sched_setscheduler(0, SCHED_FIFO, &param)) {
-                       printf("Could not set realtime priority.\n");
+                       pr_err("Could not set realtime priority.\n");
                        exit(-1);
                }
        }
 
+       /*
+        * Let the child rip
+        */
+       if (forks)
+               close(go_pipe[1]);
+
        for (;;) {
                int hits = samples;
 
                for (i = 0; i < nr_cpu; i++) {
-                       for (counter = 0; counter < nr_counters; counter++)
-                               mmap_read(&mmap_array[i][counter]);
+                       for (counter = 0; counter < nr_counters; counter++) {
+                               if (mmap_array[i][counter].base)
+                                       mmap_read(&mmap_array[i][counter]);
+                       }
                }
 
                if (hits == samples) {
                        if (done)
                                break;
-                       ret = poll(event_array, nr_poll, 100);
+                       err = poll(event_array, nr_poll, -1);
+                       waking++;
+               }
+
+               if (done) {
+                       for (i = 0; i < nr_cpu; i++) {
+                               for (counter = 0; counter < nr_counters; counter++)
+                                       ioctl(fd[i][counter], PERF_EVENT_IOC_DISABLE);
+                       }
                }
        }
 
+       fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
+
        /*
         * Approximate RIP event size: 24 bytes.
         */
@@ -646,6 +647,8 @@ static const struct option options[] = {
        OPT_CALLBACK('e', "event", NULL, "event",
                     "event selector. use 'perf list' to list available events",
                     parse_events),
+       OPT_CALLBACK(0, "filter", NULL, "filter",
+                    "event filter", parse_filter),
        OPT_INTEGER('p', "pid", &target_pid,
                    "record events on existing pid"),
        OPT_INTEGER('r', "realtime", &realtime_prio,
@@ -680,6 +683,8 @@ static const struct option options[] = {
                    "Sample addresses"),
        OPT_BOOLEAN('n', "no-samples", &no_samples,
                    "don't sample"),
+       OPT_BOOLEAN('M', "multiplex", &multiplex,
+                   "multiplex counter output in a single channel"),
        OPT_END()
 };
 
@@ -688,16 +693,30 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
        int counter;
 
        argc = parse_options(argc, argv, options, record_usage,
-               PARSE_OPT_STOP_AT_NON_OPTION);
-       if (!argc && target_pid == -1 && !system_wide)
+                           PARSE_OPT_STOP_AT_NON_OPTION);
+       if (!argc && target_pid == -1 && !system_wide && profile_cpu == -1)
                usage_with_options(record_usage, options);
 
+       symbol__init();
+
        if (!nr_counters) {
                nr_counters     = 1;
                attrs[0].type   = PERF_TYPE_HARDWARE;
                attrs[0].config = PERF_COUNT_HW_CPU_CYCLES;
        }
 
+       /*
+        * User specified count overrides default frequency.
+        */
+       if (default_interval)
+               freq = 0;
+       else if (freq) {
+               default_interval = freq;
+       } else {
+               fprintf(stderr, "frequency and count are zero, aborting\n");
+               exit(EXIT_FAILURE);
+       }
+
        for (counter = 0; counter < nr_counters; counter++) {
                if (attrs[counter].sample_period)
                        continue;