X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=tools%2Fperf%2Fbuiltin-record.c;h=a1b99eeac3c0e9887af395a969d781865aab8905;hb=84b13fd596522db47f9545d5124c30cc00dfdf5a;hp=718b8f7b6aac005e34e912fea880968d395d899d;hpb=1fe2c1066ce6a30bda7b27785ee3d9b8e62ffbbd;p=safe%2Fjmp%2Flinux-2.6 diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 718b8f7..a1b99ee 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -5,59 +5,79 @@ * (or a CPU, or a PID) into the perf.data output file - for * later analysis via perf report. */ +#define _FILE_OFFSET_BITS 64 + #include "builtin.h" #include "perf.h" +#include "util/build-id.h" #include "util/util.h" #include "util/parse-options.h" #include "util/parse-events.h" -#include "util/string.h" #include "util/header.h" +#include "util/event.h" +#include "util/debug.h" +#include "util/session.h" +#include "util/symbol.h" +#include "util/cpumap.h" #include #include -#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) -#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) +enum write_mode_t { + WRITE_FORCE, + WRITE_APPEND +}; -static int fd[MAX_NR_CPUS][MAX_COUNTERS]; +static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; -static long default_interval = 100000; +static unsigned int user_interval = UINT_MAX; +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 unsigned int user_freq = UINT_MAX; +static int freq = 1000; static int output; +static int pipe_output = 0; static const char *output_name = "perf.data"; -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 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 bool raw_samples = false; +static bool system_wide = false; +static int profile_cpu = -1; +static pid_t target_pid = -1; +static pid_t target_tid = -1; +static pid_t *all_tids = NULL; +static int thread_num = 0; +static pid_t child_pid = -1; +static bool inherit = true; +static enum write_mode_t write_mode = WRITE_FORCE; +static bool call_graph = false; +static bool inherit_stat = false; +static bool no_samples = false; +static bool sample_address = false; +static bool multiplex = false; +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 struct pollfd *event_array; -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; +static off_t post_processing_offset; -struct perf_header *header; +static struct perf_session *session; struct mmap_data { int counter; @@ -66,11 +86,11 @@ struct mmap_data { unsigned int prev; }; -static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; +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; @@ -81,7 +101,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. @@ -90,6 +110,11 @@ static void mmap_write_tail(struct mmap_data *md, unsigned long tail) pc->data_tail = tail; } +static void advance_output(size_t size) +{ + bytes_written += size; +} + static void write_output(void *buf, size_t size) { while (size) { @@ -105,6 +130,13 @@ static void write_output(void *buf, size_t size) } } +static int process_synthesized_event(event_t *event, + struct perf_session *self __used) +{ + write_output(event, event->header.size); + return 0; +} + static void mmap_read(struct mmap_data *md) { unsigned int head = mmap_read_head(md); @@ -177,6 +209,9 @@ static void sig_handler(int sig) static void sig_atexit(void) { + if (child_pid != -1) + kill(child_pid, SIGTERM); + if (signr == -1) return; @@ -184,184 +219,34 @@ static void sig_atexit(void) kill(getpid(), signr); } -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; - size_t size; - char *field, *sep; - DIR *tasks; - struct dirent dirent, *next; - - snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); - - fd = open(filename, O_RDONLY); - if (fd < 0) { - /* - * 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); - exit(EXIT_FAILURE); - } - close(fd); - - /* 9027 (cat) R 6747 9027 6747 34816 9027 ... */ - memset(&comm_ev, 0, sizeof(comm_ev)); - field = strchr(bf, '('); - if (field == NULL) - goto out_failure; - sep = strchr(++field, ')'); - if (sep == NULL) - goto out_failure; - size = sep - field; - memcpy(comm_ev.comm, field, size++); - - comm_ev.pid = pid; - 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); - return; - } - - 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); - return; - -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) -{ - 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 = pid; - 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; - - pid = strtol(dirent.d_name, &end, 10); - if (*end) /* only interested in proper numerical dirents */ - continue; - - pid_synthesize_comm_event(pid, 1); - pid_synthesize_mmap_samples(pid); - } - - 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; } -static void create_counter(int counter, int cpu, pid_t pid) +static void create_counter(int counter, int cpu) { - 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 thread_index; + int ret; struct { u64 count; u64 time_enabled; @@ -373,12 +258,24 @@ static void create_counter(int counter, int cpu, pid_t pid) PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; - attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; + attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; + + if (nr_counters > 1) + attr->sample_type |= PERF_SAMPLE_ID; - if (freq) { - attr->sample_type |= PERF_SAMPLE_PERIOD; - attr->freq = 1; - attr->sample_freq = freq; + /* + * We default some events to a 1 default interval. But keep + * it a weak assumption overridable by the user. + */ + if (!attr->sample_period || (user_freq != UINT_MAX && + user_interval != UINT_MAX)) { + if (freq) { + attr->sample_type |= PERF_SAMPLE_PERIOD; + attr->freq = 1; + attr->sample_freq = freq; + } else { + attr->sample_period = default_interval; + } } if (no_samples) @@ -393,100 +290,170 @@ static void create_counter(int counter, int cpu, pid_t pid) if (call_graph) 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->disabled = 1; + attr->inherit = inherit; + if (target_pid == -1 && !system_wide) { + attr->disabled = 1; + attr->enable_on_exec = 1; + } + for (thread_index = 0; thread_index < thread_num; thread_index++) { try_again: - fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0); + fd[nr_cpu][counter][thread_index] = sys_perf_event_open(attr, + all_tids[thread_index], cpu, group_fd, 0); + + if (fd[nr_cpu][counter][thread_index] < 0) { + int err = errno; + + if (err == EPERM || err == EACCES) + die("Permission error - are you root?\n" + "\t Consider tweaking" + " /proc/sys/kernel/perf_event_paranoid.\n"); + else if (err == ENODEV && profile_cpu != -1) { + die("No such device - did you specify" + " an out-of-range profile CPU?\n"); + } - if (fd[nr_cpu][counter] < 0) { - int err = errno; + /* + * 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][thread_index], strerror(err)); + +#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); + } - if (err == EPERM) - die("Permission error - are you root?\n"); + h_attr = get_header_attr(attr, counter); + if (h_attr == NULL) + die("nomem\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; + if (!file_new) { + if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { + fprintf(stderr, "incompatible append\n"); + exit(-1); + } } - 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); - } - h_attr = get_header_attr(attr, counter); - - if (!file_new) { - if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { - fprintf(stderr, "incompatible append\n"); + if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) { + perror("Unable to read perf file descriptor\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); - } + if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { + pr_warning("Not enough memory to add id\n"); + exit(-1); + } - perf_header_attr__add_id(h_attr, read_data.id); + assert(fd[nr_cpu][counter][thread_index] >= 0); + fcntl(fd[nr_cpu][counter][thread_index], F_SETFL, O_NONBLOCK); - assert(fd[nr_cpu][counter] >= 0); - fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); + /* + * First counter acts as the group leader: + */ + if (group && group_fd == -1) + group_fd = fd[nr_cpu][counter][thread_index]; + if (multiplex && multiplex_fd == -1) + multiplex_fd = fd[nr_cpu][counter][thread_index]; + + if (multiplex && fd[nr_cpu][counter][thread_index] != multiplex_fd) { + + ret = ioctl(fd[nr_cpu][counter][thread_index], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); + assert(ret != -1); + } else { + event_array[nr_poll].fd = fd[nr_cpu][counter][thread_index]; + event_array[nr_poll].events = POLLIN; + nr_poll++; + + mmap_array[nr_cpu][counter][thread_index].counter = counter; + mmap_array[nr_cpu][counter][thread_index].prev = 0; + mmap_array[nr_cpu][counter][thread_index].mask = mmap_pages*page_size - 1; + mmap_array[nr_cpu][counter][thread_index].base = mmap(NULL, (mmap_pages+1)*page_size, + PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter][thread_index], 0); + if (mmap_array[nr_cpu][counter][thread_index].base == MAP_FAILED) { + error("failed to mmap with %d (%s)\n", errno, strerror(errno)); + exit(-1); + } + } - /* - * First counter acts as the group leader: - */ - if (group && group_fd == -1) - group_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 (filter != NULL) { + ret = ioctl(fd[nr_cpu][counter][thread_index], + 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_COUNTER_IOC_ENABLE); } -static void open_counters(int cpu, pid_t pid) +static void open_counters(int cpu) { int counter; group_fd = -1; for (counter = 0; counter < nr_counters; counter++) - create_counter(counter, cpu, pid); + create_counter(counter, cpu); nr_cpu++; } +static int process_buildids(void) +{ + u64 size = lseek(output, 0, SEEK_CUR); + + if (size == 0) + return 0; + + session->fd = output; + return __perf_session__process_events(session, post_processing_offset, + size - post_processing_offset, + size, &build_id__mark_dso_hit_ops); +} + static void atexit_header(void) { - header->data_size += bytes_written; + if (!pipe_output) { + session->header.data_size += bytes_written; - perf_header__write(header, output); + process_buildids(); + perf_header__write(&session->header, output, true); + } else { + int err; + + err = event__synthesize_build_ids(process_synthesized_event, + session); + if (err < 0) + pr_err("Couldn't synthesize build ids.\n"); + } } static int __cmd_record(int argc, const char **argv) @@ -495,102 +462,256 @@ 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 = argc > 0; + char buf; page_size = sysconf(_SC_PAGE_SIZE); - nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); - 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) && 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 (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { + perror("failed to create pipes"); + exit(-1); + } + + if (!strcmp(output_name, "-")) + pipe_output = 1; + else if (!stat(output_name, &st) && st.st_size) { + if (write_mode == WRITE_FORCE) { + char oldname[PATH_MAX]; + snprintf(oldname, sizeof(oldname), "%s.old", + output_name); + unlink(oldname); + rename(output_name, oldname); } - } else { - append_file = 0; + } else if (write_mode == WRITE_APPEND) { + write_mode = WRITE_FORCE; } flags = O_CREAT|O_RDWR; - if (append_file) + if (write_mode == WRITE_APPEND) file_new = 0; else flags |= O_TRUNC; - output = open(output_name, flags, S_IRUSR|S_IWUSR); + if (pipe_output) + output = STDOUT_FILENO; + else + output = open(output_name, flags, S_IRUSR | S_IWUSR); if (output < 0) { perror("failed to create output file"); exit(-1); } - if (!file_new) - header = perf_header__read(output); - else - header = perf_header__new(); + session = perf_session__new(output_name, O_WRONLY, + write_mode == WRITE_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, output); + if (err < 0) + return err; + } + + if (raw_samples) { + 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) { + 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) { + child_pid = fork(); + if (pid < 0) { + perror("failed to fork"); + exit(-1); + } - open_counters(-1, pid); - } else for (i = 0; i < nr_cpus; i++) - open_counters(i, target_pid); + if (!child_pid) { + if (pipe_output) + dup2(2, 1); + 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); + } - if (file_new) - perf_header__write(header, output); + if (!system_wide && target_tid == -1 && target_pid == -1) + all_tids[0] = child_pid; - if (!system_wide) { - pid_synthesize_comm_event(pid, 0); - pid_synthesize_mmap_samples(pid); - } else - synthesize_all(); + 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 (target_pid == -1 && argc) { - pid = fork(); - if (pid < 0) - perror("failed to fork"); + if ((!system_wide && !inherit) || profile_cpu != -1) { + open_counters(profile_cpu); + } else { + nr_cpus = read_cpu_map(); + for (i = 0; i < nr_cpus; i++) + open_counters(cpumap[i]); + } - if (!pid) { - if (execvp(argv[0], (char **)argv)) { - perror(argv[0]); - exit(-1); - } + if (pipe_output) { + err = perf_header__write_pipe(output); + if (err < 0) + return err; + } else if (file_new) { + err = perf_header__write(&session->header, output, false); + if (err < 0) + return err; + } + + post_processing_offset = lseek(output, 0, SEEK_CUR); + + if (pipe_output) { + err = event__synthesize_attrs(&session->header, + process_synthesized_event, + session); + if (err < 0) { + pr_err("Couldn't synthesize attrs.\n"); + return err; } + + err = event__synthesize_event_types(process_synthesized_event, + session); + if (err < 0) { + pr_err("Couldn't synthesize event_types.\n"); + return err; + } + + err = event__synthesize_tracing_data(output, attrs, + nr_counters, + process_synthesized_event, + session); + if (err <= 0) { + pr_err("Couldn't record tracing data.\n"); + return err; + } + + advance_output(err); } + err = event__synthesize_kernel_mmap(process_synthesized_event, + session, "_text"); + if (err < 0) + err = event__synthesize_kernel_mmap(process_synthesized_event, + session, "_stext"); + 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(target_tid, 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, ¶m)) { - 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; + int thread; 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++) { + for (thread = 0; + thread < thread_num; thread++) { + if (mmap_array[i][counter][thread].base) + mmap_read(&mmap_array[i][counter][thread]); + } + + } } 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++) { + for (thread = 0; + thread < thread_num; + thread++) + ioctl(fd[i][counter][thread], + PERF_EVENT_IOC_DISABLE); + } + } } } + fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); + /* * Approximate RIP event size: 24 bytes. */ @@ -609,33 +730,43 @@ static const char * const record_usage[] = { NULL }; +static bool force, append_file; + 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"), + "record events on existing process id"), + OPT_INTEGER('t', "tid", &target_tid, + "record events on existing thread id"), OPT_INTEGER('r', "realtime", &realtime_prio, "collect data with this RT SCHED_FIFO priority"), + OPT_BOOLEAN('R', "raw-samples", &raw_samples, + "collect raw sample records from all opened counters"), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), OPT_BOOLEAN('A', "append", &append_file, "append to the output file to do incremental profiling"), + OPT_INTEGER('C', "profile_cpu", &profile_cpu, + "CPU to profile on"), OPT_BOOLEAN('f', "force", &force, - "overwrite existing data file"), - OPT_LONG('c', "count", &default_interval, + "overwrite existing data file (deprecated)"), + OPT_LONG('c', "count", &user_interval, "event period to sample"), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_BOOLEAN('i', "inherit", &inherit, "child tasks inherit counters"), - OPT_INTEGER('F', "freq", &freq, + OPT_INTEGER('F', "freq", &user_freq, "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, + OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_BOOLEAN('s', "stat", &inherit_stat, "per thread counts"), @@ -643,29 +774,85 @@ 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() }; int cmd_record(int argc, const char **argv, const char *prefix __used) { - int counter; + int i,j; 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 && target_tid == -1 && + !system_wide && profile_cpu == -1) usage_with_options(record_usage, options); + if (force && append_file) { + fprintf(stderr, "Can't overwrite and append at the same time." + " You need to choose between -f and -A"); + usage_with_options(record_usage, options); + } else if (append_file) { + write_mode = WRITE_APPEND; + } else { + write_mode = WRITE_FORCE; + } + + symbol__init(); + 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) - continue; + if (target_pid != -1) { + target_tid = target_pid; + thread_num = find_all_tid(target_pid, &all_tids); + if (thread_num <= 0) { + fprintf(stderr, "Can't find all threads of pid %d\n", + target_pid); + usage_with_options(record_usage, options); + } + } else { + all_tids=malloc(sizeof(pid_t)); + if (!all_tids) + return -ENOMEM; - attrs[counter].sample_period = default_interval; + all_tids[0] = target_tid; + thread_num = 1; + } + + for (i = 0; i < MAX_NR_CPUS; i++) { + for (j = 0; j < MAX_COUNTERS; j++) { + fd[i][j] = malloc(sizeof(int)*thread_num); + mmap_array[i][j] = zalloc( + sizeof(struct mmap_data)*thread_num); + if (!fd[i][j] || !mmap_array[i][j]) + return -ENOMEM; + } + } + event_array = malloc( + sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); + if (!event_array) + return -ENOMEM; + + if (user_interval != UINT_MAX) + default_interval = user_interval; + if (user_freq != UINT_MAX) + freq = user_freq; + + /* + * 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); } return __cmd_record(argc, argv);