perf record: Fix use of sample_id_all userspace with !sample_id_all kernels
[pandora-kernel.git] / tools / perf / builtin-record.c
index 93bd2ff..50efbd5 100644 (file)
@@ -36,6 +36,7 @@ static int                    *fd[MAX_NR_CPUS][MAX_COUNTERS];
 
 static u64                     user_interval                   = ULLONG_MAX;
 static u64                     default_interval                =      0;
+static u64                     sample_type;
 
 static int                     nr_cpus                         =      0;
 static unsigned int            page_size;
@@ -48,6 +49,7 @@ static const char             *output_name                    = "perf.data";
 static int                     group                           =      0;
 static int                     realtime_prio                   =      0;
 static bool                    raw_samples                     =  false;
+static bool                    sample_id_all_avail             =   true;
 static bool                    system_wide                     =  false;
 static pid_t                   target_pid                      =     -1;
 static pid_t                   target_tid                      =     -1;
@@ -60,7 +62,9 @@ static bool                   call_graph                      =  false;
 static bool                    inherit_stat                    =  false;
 static bool                    no_samples                      =  false;
 static bool                    sample_address                  =  false;
+static bool                    sample_time                     =  false;
 static bool                    no_buildid                      =  false;
+static bool                    no_buildid_cache                =  false;
 
 static long                    samples                         =      0;
 static u64                     bytes_written                   =      0;
@@ -128,6 +132,7 @@ static void write_output(void *buf, size_t size)
 }
 
 static int process_synthesized_event(event_t *event,
+                                    struct sample_data *sample __used,
                                     struct perf_session *self __used)
 {
        write_output(event, event->header.size);
@@ -197,7 +202,7 @@ static void sig_atexit(void)
        if (child_pid > 0)
                kill(child_pid, SIGTERM);
 
-       if (signr == -1)
+       if (signr == -1 || signr == SIGUSR1)
                return;
 
        signal(signr, SIG_DFL);
@@ -238,6 +243,19 @@ static void create_counter(int counter, int cpu)
                u64 time_running;
                u64 id;
        } read_data;
+       /*
+        * Check if parse_single_tracepoint_event has already asked for
+        * PERF_SAMPLE_TIME.
+        *
+        * XXX this is kludgy but short term fix for problems introduced by
+        * eac23d1c that broke 'perf script' by having different sample_types
+        * when using multiple tracepoint events when we use a perf binary
+        * that tries to use sample_id_all on an older kernel.
+        *
+        * We need to move counter creation to perf_session, support
+        * different sample_types, etc.
+        */
+       bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
 
        attr->read_format       = PERF_FORMAT_TOTAL_TIME_ENABLED |
                                  PERF_FORMAT_TOTAL_TIME_RUNNING |
@@ -280,6 +298,10 @@ static void create_counter(int counter, int cpu)
        if (system_wide)
                attr->sample_type       |= PERF_SAMPLE_CPU;
 
+       if (sample_id_all_avail &&
+           (sample_time || system_wide || !no_inherit || cpu_list))
+               attr->sample_type       |= PERF_SAMPLE_TIME;
+
        if (raw_samples) {
                attr->sample_type       |= PERF_SAMPLE_TIME;
                attr->sample_type       |= PERF_SAMPLE_RAW;
@@ -293,6 +315,8 @@ static void create_counter(int counter, int cpu)
                attr->disabled = 1;
                attr->enable_on_exec = 1;
        }
+retry_sample_id:
+       attr->sample_id_all = sample_id_all_avail ? 1 : 0;
 
        for (thread_index = 0; thread_index < thread_num; thread_index++) {
 try_again:
@@ -309,6 +333,15 @@ try_again:
                        else if (err ==  ENODEV && cpu_list) {
                                die("No such device - did you specify"
                                        " an out-of-range profile CPU?\n");
+                       } else if (err == EINVAL && sample_id_all_avail) {
+                               /*
+                                * Old kernel, no attr->sample_id_type_all field
+                                */
+                               sample_id_all_avail = false;
+                               if (!sample_time && !raw_samples && !time_needed)
+                                       attr->sample_type &= ~PERF_SAMPLE_TIME;
+
+                               goto retry_sample_id;
                        }
 
                        /*
@@ -326,7 +359,7 @@ try_again:
                                goto try_again;
                        }
                        printf("\n");
-                       error("perfcounter syscall returned with %d (%s)\n",
+                       error("sys_perf_event_open() syscall returned with %d (%s).  /bin/dmesg may provide additional information.\n",
                                        fd[nr_cpu][counter][thread_index], strerror(err));
 
 #if defined(__i386__) || defined(__x86_64__)
@@ -406,6 +439,9 @@ try_again:
                        }
                }
        }
+
+       if (!sample_type)
+               sample_type = attr->sample_type;
 }
 
 static void open_counters(int cpu)
@@ -437,7 +473,8 @@ static void atexit_header(void)
        if (!pipe_output) {
                session->header.data_size += bytes_written;
 
-               process_buildids();
+               if (!no_buildid)
+                       process_buildids();
                perf_header__write(&session->header, output, true);
                perf_session__delete(session);
                symbol__exit();
@@ -515,6 +552,7 @@ static int __cmd_record(int argc, const char **argv)
        atexit(sig_atexit);
        signal(SIGCHLD, sig_handler);
        signal(SIGINT, sig_handler);
+       signal(SIGUSR1, sig_handler);
 
        if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
                perror("failed to create pipes");
@@ -551,12 +589,15 @@ static int __cmd_record(int argc, const char **argv)
        }
 
        session = perf_session__new(output_name, O_WRONLY,
-                                   write_mode == WRITE_FORCE, false);
+                                   write_mode == WRITE_FORCE, false, NULL);
        if (session == NULL) {
                pr_err("Not enough memory for reading perf file header\n");
                return -1;
        }
 
+       if (!no_buildid)
+               perf_header__set_feat(&session->header, HEADER_BUILD_ID);
+
        if (!file_new) {
                err = perf_header__read(session, output);
                if (err < 0)
@@ -606,6 +647,7 @@ static int __cmd_record(int argc, const char **argv)
                        execvp(argv[0], (char **)argv);
 
                        perror(argv[0]);
+                       kill(getppid(), SIGUSR1);
                        exit(-1);
                }
 
@@ -637,6 +679,8 @@ static int __cmd_record(int argc, const char **argv)
                        open_counters(cpumap[i]);
        }
 
+       perf_session__set_sample_type(session, sample_type);
+
        if (pipe_output) {
                err = perf_header__write_pipe(output);
                if (err < 0)
@@ -649,6 +693,8 @@ static int __cmd_record(int argc, const char **argv)
 
        post_processing_offset = lseek(output, 0, SEEK_CUR);
 
+       perf_session__set_sample_id_all(session, sample_id_all_avail);
+
        if (pipe_output) {
                err = event__synthesize_attrs(&session->header,
                                              process_synthesized_event,
@@ -697,17 +743,18 @@ static int __cmd_record(int argc, const char **argv)
        if (err < 0)
                err = event__synthesize_kernel_mmap(process_synthesized_event,
                                                    session, machine, "_stext");
-       if (err < 0) {
-               pr_err("Couldn't record kernel reference relocation symbol.\n");
-               return err;
-       }
+       if (err < 0)
+               pr_err("Couldn't record kernel reference relocation symbol\n"
+                      "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
+                      "Check /proc/kallsyms permission or run as root.\n");
 
        err = event__synthesize_modules(process_synthesized_event,
                                        session, machine);
-       if (err < 0) {
-               pr_err("Couldn't record kernel reference relocation symbol.\n");
-               return err;
-       }
+       if (err < 0)
+               pr_err("Couldn't record kernel module information.\n"
+                      "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
+                      "Check /proc/modules permission or run as root.\n");
+
        if (perf_guest)
                perf_session__process_machines(session, event__synthesize_guest_os);
 
@@ -761,7 +808,7 @@ static int __cmd_record(int argc, const char **argv)
                }
        }
 
-       if (quiet)
+       if (quiet || signr == SIGUSR1)
                return 0;
 
        fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
@@ -828,10 +875,13 @@ const struct option record_options[] = {
                    "per thread counts"),
        OPT_BOOLEAN('d', "data", &sample_address,
                    "Sample addresses"),
+       OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"),
        OPT_BOOLEAN('n', "no-samples", &no_samples,
                    "don't sample"),
-       OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid,
+       OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache,
                    "do not update the buildid cache"),
+       OPT_BOOLEAN('B', "no-buildid", &no_buildid,
+                   "do not collect buildids in perf.data"),
        OPT_END()
 };
 
@@ -856,7 +906,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
        }
 
        symbol__init();
-       if (no_buildid)
+
+       if (no_buildid_cache || no_buildid)
                disable_buildid_cache();
 
        if (!nr_counters) {