aboutsummaryrefslogtreecommitdiff
path: root/tools/perf/builtin-record.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r--tools/perf/builtin-record.c1339
1 files changed, 685 insertions, 654 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 93bd2ff001f..378b85b731a 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -5,8 +5,6 @@
* (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"
@@ -18,150 +16,68 @@
#include "util/header.h"
#include "util/event.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
#include "util/debug.h"
#include "util/session.h"
+#include "util/tool.h"
#include "util/symbol.h"
#include "util/cpumap.h"
+#include "util/thread_map.h"
+#include "util/data.h"
#include <unistd.h>
#include <sched.h>
#include <sys/mman.h>
-enum write_mode_t {
- WRITE_FORCE,
- WRITE_APPEND
-};
-static int *fd[MAX_NR_CPUS][MAX_COUNTERS];
-
-static u64 user_interval = ULLONG_MAX;
-static u64 default_interval = 0;
-
-static int nr_cpus = 0;
-static unsigned int page_size;
-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 int realtime_prio = 0;
-static bool raw_samples = false;
-static bool system_wide = false;
-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 no_inherit = false;
-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 no_buildid = false;
-
-static long samples = 0;
-static u64 bytes_written = 0;
-
-static struct pollfd *event_array;
-
-static int nr_poll = 0;
-static int nr_cpu = 0;
-
-static int file_new = 1;
-static off_t post_processing_offset;
-
-static struct perf_session *session;
-static const char *cpu_list;
-
-struct mmap_data {
- int counter;
- void *base;
- unsigned int mask;
- unsigned int prev;
+struct record {
+ struct perf_tool tool;
+ struct record_opts opts;
+ u64 bytes_written;
+ struct perf_data_file file;
+ struct perf_evlist *evlist;
+ struct perf_session *session;
+ const char *progname;
+ int realtime_prio;
+ bool no_buildid;
+ bool no_buildid_cache;
+ long samples;
};
-static struct mmap_data mmap_array[MAX_NR_CPUS];
-
-static unsigned long mmap_read_head(struct mmap_data *md)
-{
- struct perf_event_mmap_page *pc = md->base;
- long head;
-
- head = pc->data_head;
- rmb();
-
- return head;
-}
-
-static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
+static int record__write(struct record *rec, void *bf, size_t size)
{
- struct perf_event_mmap_page *pc = md->base;
-
- /*
- * ensure all reads are done before we write the tail out.
- */
- /* mb(); */
- 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) {
- int ret = write(output, buf, size);
-
- if (ret < 0)
- die("failed to write");
-
- size -= ret;
- buf += ret;
-
- bytes_written += ret;
+ if (perf_data_file__write(rec->session->file, bf, size) < 0) {
+ pr_err("failed to write perf data, error: %m\n");
+ return -1;
}
+
+ rec->bytes_written += size;
+ return 0;
}
-static int process_synthesized_event(event_t *event,
- struct perf_session *self __used)
+static int process_synthesized_event(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
{
- write_output(event, event->header.size);
- return 0;
+ struct record *rec = container_of(tool, struct record, tool);
+ return record__write(rec, event, event->header.size);
}
-static void mmap_read(struct mmap_data *md)
+static int record__mmap_read(struct record *rec, struct perf_mmap *md)
{
- unsigned int head = mmap_read_head(md);
+ unsigned int head = perf_mmap__read_head(md);
unsigned int old = md->prev;
unsigned char *data = md->base + page_size;
unsigned long size;
void *buf;
- int diff;
+ int rc = 0;
- /*
- * If we're further behind than half the buffer, there's a chance
- * the writer will bite our tail and mess up the samples under us.
- *
- * If we somehow ended up ahead of the head, we got messed up.
- *
- * In either case, truncate and restart at head.
- */
- diff = head - old;
- if (diff < 0) {
- fprintf(stderr, "WARNING: failed to keep up with mmap data\n");
- /*
- * head points to a known good entry, start there.
- */
- old = head;
- }
+ if (old == head)
+ return 0;
- if (old != head)
- samples++;
+ rec->samples++;
size = head - old;
@@ -170,288 +86,126 @@ static void mmap_read(struct mmap_data *md)
size = md->mask + 1 - (old & md->mask);
old += size;
- write_output(buf, size);
+ if (record__write(rec, buf, size) < 0) {
+ rc = -1;
+ goto out;
+ }
}
buf = &data[old & md->mask];
size = head - old;
old += size;
- write_output(buf, size);
+ if (record__write(rec, buf, size) < 0) {
+ rc = -1;
+ goto out;
+ }
md->prev = old;
- mmap_write_tail(md, old);
+ perf_mmap__write_tail(md, old);
+
+out:
+ return rc;
}
static volatile int done = 0;
static volatile int signr = -1;
+static volatile int child_finished = 0;
static void sig_handler(int sig)
{
+ if (sig == SIGCHLD)
+ child_finished = 1;
+ else
+ signr = sig;
+
done = 1;
- signr = sig;
}
-static void sig_atexit(void)
+static void record__sig_exit(void)
{
- if (child_pid > 0)
- kill(child_pid, SIGTERM);
-
if (signr == -1)
return;
signal(signr, SIG_DFL);
- kill(getpid(), signr);
+ raise(signr);
}
-static int group_fd;
-
-static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)
+static int record__open(struct record *rec)
{
- struct perf_header_attr *h_attr;
+ char msg[512];
+ struct perf_evsel *pos;
+ struct perf_evlist *evlist = rec->evlist;
+ struct perf_session *session = rec->session;
+ struct record_opts *opts = &rec->opts;
+ int rc = 0;
- if (nr < session->header.attrs) {
- h_attr = session->header.attr[nr];
- } else {
- h_attr = perf_header_attr__new(a);
- 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)
-{
- 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;
- 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;
-
- if (nr_counters > 1)
- attr->sample_type |= PERF_SAMPLE_ID;
-
- /*
- * 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 != ULLONG_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)
- attr->sample_freq = 0;
+ perf_evlist__config(evlist, opts);
- if (inherit_stat)
- attr->inherit_stat = 1;
-
- if (sample_address) {
- attr->sample_type |= PERF_SAMPLE_ADDR;
- attr->mmap_data = track;
- }
-
- if (call_graph)
- attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
-
- if (system_wide)
- attr->sample_type |= PERF_SAMPLE_CPU;
-
- 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 = !no_inherit;
- if (target_pid == -1 && target_tid == -1 && !system_wide) {
- attr->disabled = 1;
- attr->enable_on_exec = 1;
- }
-
- for (thread_index = 0; thread_index < thread_num; thread_index++) {
+ evlist__for_each(evlist, pos) {
try_again:
- 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 && cpu_list) {
- die("No such device - did you specify"
- " an out-of-range profile CPU?\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 (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {
+ if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) {
if (verbose)
- warning(" ... trying to fall back to cpu-clock-ticks\n");
- attr->type = PERF_TYPE_SOFTWARE;
- attr->config = PERF_COUNT_SW_CPU_CLOCK;
+ ui__warning("%s\n", msg);
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);
- }
-
- 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))) {
- fprintf(stderr, "incompatible append\n");
- exit(-1);
- }
- }
-
- if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) {
- perror("Unable to read perf file descriptor");
- 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);
+ rc = -errno;
+ perf_evsel__open_strerror(pos, &opts->target,
+ errno, msg, sizeof(msg));
+ ui__error("%s\n", msg);
+ goto out;
}
+ }
- assert(fd[nr_cpu][counter][thread_index] >= 0);
- fcntl(fd[nr_cpu][counter][thread_index], F_SETFL, O_NONBLOCK);
+ if (perf_evlist__apply_filters(evlist)) {
+ error("failed to set filter with %d (%s)\n", errno,
+ strerror(errno));
+ rc = -1;
+ goto out;
+ }
- /*
- * First counter acts as the group leader:
- */
- if (group && group_fd == -1)
- group_fd = fd[nr_cpu][counter][thread_index];
-
- if (counter || thread_index) {
- ret = ioctl(fd[nr_cpu][counter][thread_index],
- PERF_EVENT_IOC_SET_OUTPUT,
- fd[nr_cpu][0][0]);
- if (ret) {
- error("failed to set output: %d (%s)\n", errno,
- strerror(errno));
- exit(-1);
- }
+ if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) {
+ if (errno == EPERM) {
+ pr_err("Permission error mapping pages.\n"
+ "Consider increasing "
+ "/proc/sys/kernel/perf_event_mlock_kb,\n"
+ "or try again with a smaller value of -m/--mmap_pages.\n"
+ "(current value: %u)\n", opts->mmap_pages);
+ rc = -errno;
} else {
- mmap_array[nr_cpu].counter = counter;
- mmap_array[nr_cpu].prev = 0;
- mmap_array[nr_cpu].mask = mmap_pages*page_size - 1;
- mmap_array[nr_cpu].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].base == MAP_FAILED) {
- error("failed to mmap with %d (%s)\n", errno, strerror(errno));
- exit(-1);
- }
-
- event_array[nr_poll].fd = fd[nr_cpu][counter][thread_index];
- event_array[nr_poll].events = POLLIN;
- nr_poll++;
- }
-
- 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);
- }
+ pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno));
+ rc = -errno;
}
+ goto out;
}
-}
-
-static void open_counters(int cpu)
-{
- int counter;
-
- group_fd = -1;
- for (counter = 0; counter < nr_counters; counter++)
- create_counter(counter, cpu);
- nr_cpu++;
+ session->evlist = evlist;
+ perf_session__set_id_hdr_size(session);
+out:
+ return rc;
}
-static int process_buildids(void)
+static int process_buildids(struct record *rec)
{
- u64 size = lseek(output, 0, SEEK_CUR);
+ struct perf_data_file *file = &rec->file;
+ struct perf_session *session = rec->session;
+ u64 start = session->header.data_offset;
+ u64 size = lseek(file->fd, 0, SEEK_CUR);
if (size == 0)
return 0;
- session->fd = output;
- return __perf_session__process_events(session, post_processing_offset,
- size - post_processing_offset,
+ return __perf_session__process_events(session, start,
+ size - start,
size, &build_id__mark_dso_hit_ops);
}
-static void atexit_header(void)
-{
- if (!pipe_output) {
- session->header.data_size += bytes_written;
-
- process_buildids();
- perf_header__write(&session->header, output, true);
- perf_session__delete(session);
- symbol__exit();
- }
-}
-
-static void event__synthesize_guest_os(struct machine *machine, void *data)
+static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
{
int err;
- struct perf_session *psession = data;
-
- if (machine__is_host(machine))
- return;
-
+ struct perf_tool *tool = data;
/*
*As for guest kernel when processing subcommand record&report,
*we arrange module mmap prior to guest kernel mmap and trigger
@@ -460,8 +214,8 @@ static void event__synthesize_guest_os(struct machine *machine, void *data)
*method is used to avoid symbol missing when the first addr is
*in module instead of in guest kernel.
*/
- err = event__synthesize_modules(process_synthesized_event,
- psession, machine);
+ err = perf_event__synthesize_modules(tool, process_synthesized_event,
+ machine);
if (err < 0)
pr_err("Couldn't record guest kernel [%d]'s reference"
" relocation symbol.\n", machine->pid);
@@ -470,11 +224,8 @@ static void event__synthesize_guest_os(struct machine *machine, void *data)
* We use _stext for guest kernel because guest kernel's /proc/kallsyms
* have no _text sometimes.
*/
- err = event__synthesize_kernel_mmap(process_synthesized_event,
- psession, machine, "_text");
- if (err < 0)
- err = event__synthesize_kernel_mmap(process_synthesized_event,
- psession, machine, "_stext");
+ err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
+ machine);
if (err < 0)
pr_err("Couldn't record guest kernel [%d]'s reference"
" relocation symbol.\n", machine->pid);
@@ -485,187 +236,140 @@ static struct perf_event_header finished_round_event = {
.type = PERF_RECORD_FINISHED_ROUND,
};
-static void mmap_read_all(void)
+static int record__mmap_read_all(struct record *rec)
{
int i;
+ int rc = 0;
- for (i = 0; i < nr_cpu; i++) {
- if (mmap_array[i].base)
- mmap_read(&mmap_array[i]);
+ for (i = 0; i < rec->evlist->nr_mmaps; i++) {
+ if (rec->evlist->mmap[i].base) {
+ if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) {
+ rc = -1;
+ goto out;
+ }
+ }
}
- if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
- write_output(&finished_round_event, sizeof(finished_round_event));
+ if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA))
+ rc = record__write(rec, &finished_round_event, sizeof(finished_round_event));
+
+out:
+ return rc;
}
-static int __cmd_record(int argc, const char **argv)
+static void record__init_features(struct record *rec)
+{
+ struct perf_session *session = rec->session;
+ int feat;
+
+ for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++)
+ perf_header__set_feat(&session->header, feat);
+
+ if (rec->no_buildid)
+ perf_header__clear_feat(&session->header, HEADER_BUILD_ID);
+
+ if (!have_tracepoints(&rec->evlist->entries))
+ perf_header__clear_feat(&session->header, HEADER_TRACING_DATA);
+
+ if (!rec->opts.branch_stack)
+ perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
+}
+
+static volatile int workload_exec_errno;
+
+/*
+ * perf_evlist__prepare_workload will send a SIGUSR1
+ * if the fork fails, since we asked by setting its
+ * want_signal to true.
+ */
+static void workload_exec_failed_signal(int signo __maybe_unused,
+ siginfo_t *info,
+ void *ucontext __maybe_unused)
+{
+ workload_exec_errno = info->si_value.sival_int;
+ done = 1;
+ child_finished = 1;
+}
+
+static int __cmd_record(struct record *rec, int argc, const char **argv)
{
- int i, counter;
- struct stat st;
- int flags;
int err;
+ int status = 0;
unsigned long waking = 0;
- int child_ready_pipe[2], go_pipe[2];
const bool forks = argc > 0;
- char buf;
struct machine *machine;
+ struct perf_tool *tool = &rec->tool;
+ struct record_opts *opts = &rec->opts;
+ struct perf_data_file *file = &rec->file;
+ struct perf_session *session;
+ bool disabled = false;
- page_size = sysconf(_SC_PAGE_SIZE);
+ rec->progname = argv[0];
- atexit(sig_atexit);
+ atexit(record__sig_exit);
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
- 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 if (write_mode == WRITE_APPEND) {
- write_mode = WRITE_FORCE;
- }
-
- flags = O_CREAT|O_RDWR;
- if (write_mode == WRITE_APPEND)
- file_new = 0;
- else
- flags |= O_TRUNC;
-
- 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);
- }
-
- session = perf_session__new(output_name, O_WRONLY,
- write_mode == WRITE_FORCE, false);
+ session = perf_session__new(file, false, NULL);
if (session == NULL) {
- pr_err("Not enough memory for reading perf file header\n");
+ pr_err("Perf session creation failed.\n");
return -1;
}
- if (!file_new) {
- err = perf_header__read(session, output);
- if (err < 0)
- goto out_delete_session;
- }
+ rec->session = session;
- if (have_tracepoints(attrs, nr_counters))
- perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
-
- /*
- * perf_session__delete(session) will be called at atexit_header()
- */
- atexit(atexit_header);
+ record__init_features(rec);
if (forks) {
- child_pid = fork();
- if (child_pid < 0) {
- perror("failed to fork");
- exit(-1);
- }
-
- 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 (!system_wide && target_tid == -1 && target_pid == -1)
- all_tids[0] = child_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);
+ err = perf_evlist__prepare_workload(rec->evlist, &opts->target,
+ argv, file->is_pipe,
+ workload_exec_failed_signal);
+ if (err < 0) {
+ pr_err("Couldn't run the workload!\n");
+ status = err;
+ goto out_delete_session;
}
- close(child_ready_pipe[0]);
}
- nr_cpus = read_cpu_map(cpu_list);
- if (nr_cpus < 1) {
- perror("failed to collect number of CPUs");
- return -1;
+ if (record__open(rec) != 0) {
+ err = -1;
+ goto out_child;
}
- if (!system_wide && no_inherit && !cpu_list) {
- open_counters(-1);
- } else {
- for (i = 0; i < nr_cpus; i++)
- open_counters(cpumap[i]);
- }
+ if (!rec->evlist->nr_groups)
+ perf_header__clear_feat(&session->header, HEADER_GROUP_DESC);
- if (pipe_output) {
- err = perf_header__write_pipe(output);
+ if (file->is_pipe) {
+ err = perf_header__write_pipe(file->fd);
if (err < 0)
- return err;
- } else if (file_new) {
- err = perf_header__write(&session->header, output, false);
+ goto out_child;
+ } else {
+ err = perf_session__write_header(session, rec->evlist,
+ file->fd, false);
if (err < 0)
- return err;
+ goto out_child;
}
- post_processing_offset = lseek(output, 0, SEEK_CUR);
+ if (!rec->no_buildid
+ && !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) {
+ pr_err("Couldn't generate buildids. "
+ "Use --no-buildid to profile anyway.\n");
+ err = -1;
+ goto out_child;
+ }
- 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;
- }
+ machine = &session->machines.host;
- err = event__synthesize_event_types(process_synthesized_event,
- session);
+ if (file->is_pipe) {
+ err = perf_event__synthesize_attrs(tool, session,
+ process_synthesized_event);
if (err < 0) {
- pr_err("Couldn't synthesize event_types.\n");
- return err;
+ pr_err("Couldn't synthesize attrs.\n");
+ goto out_child;
}
- if (have_tracepoints(attrs, nr_counters)) {
+ if (have_tracepoints(&rec->evlist->entries)) {
/*
* FIXME err <= 0 here actually means that
* there were no tracepoints so its not really
@@ -674,112 +378,384 @@ static int __cmd_record(int argc, const char **argv)
* return this more properly and also
* propagate errors that now are calling die()
*/
- err = event__synthesize_tracing_data(output, attrs,
- nr_counters,
- process_synthesized_event,
- session);
+ err = perf_event__synthesize_tracing_data(tool, file->fd, rec->evlist,
+ process_synthesized_event);
if (err <= 0) {
pr_err("Couldn't record tracing data.\n");
- return err;
+ goto out_child;
}
- advance_output(err);
+ rec->bytes_written += err;
}
}
- machine = perf_session__find_host_machine(session);
- if (!machine) {
- pr_err("Couldn't find native kernel information.\n");
- return -1;
- }
+ err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
+ machine);
+ 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_kernel_mmap(process_synthesized_event,
- session, machine, "_text");
+ err = perf_event__synthesize_modules(tool, process_synthesized_event,
+ machine);
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;
- }
+ 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");
- 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 (perf_guest) {
+ machines__process_guests(&session->machines,
+ perf_event__synthesize_guest_os, tool);
}
- if (perf_guest)
- perf_session__process_machines(session, event__synthesize_guest_os);
- if (!system_wide)
- event__synthesize_thread(target_tid, process_synthesized_event,
- session);
- else
- event__synthesize_threads(process_synthesized_event, session);
+ err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads,
+ process_synthesized_event, opts->sample_address);
+ if (err != 0)
+ goto out_child;
- if (realtime_prio) {
+ if (rec->realtime_prio) {
struct sched_param param;
- param.sched_priority = realtime_prio;
+ param.sched_priority = rec->realtime_prio;
if (sched_setscheduler(0, SCHED_FIFO, &param)) {
pr_err("Could not set realtime priority.\n");
- exit(-1);
+ err = -1;
+ goto out_child;
}
}
/*
+ * When perf is starting the traced process, all the events
+ * (apart from group members) have enable_on_exec=1 set,
+ * so don't spoil it by prematurely enabling them.
+ */
+ if (!target__none(&opts->target) && !opts->initial_delay)
+ perf_evlist__enable(rec->evlist);
+
+ /*
* Let the child rip
*/
if (forks)
- close(go_pipe[1]);
+ perf_evlist__start_workload(rec->evlist);
+
+ if (opts->initial_delay) {
+ usleep(opts->initial_delay * 1000);
+ perf_evlist__enable(rec->evlist);
+ }
for (;;) {
- int hits = samples;
- int thread;
+ int hits = rec->samples;
- mmap_read_all();
+ if (record__mmap_read_all(rec) < 0) {
+ err = -1;
+ goto out_child;
+ }
- if (hits == samples) {
+ if (hits == rec->samples) {
if (done)
break;
- err = poll(event_array, nr_poll, -1);
+ err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1);
+ /*
+ * Propagate error, only if there's any. Ignore positive
+ * number of returned events and interrupt error.
+ */
+ if (err > 0 || (err < 0 && errno == EINTR))
+ err = 0;
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);
- }
- }
+ /*
+ * When perf is starting the traced process, at the end events
+ * die with the process and we wait for that. Thus no need to
+ * disable events in this case.
+ */
+ if (done && !disabled && !target__none(&opts->target)) {
+ perf_evlist__disable(rec->evlist);
+ disabled = true;
}
}
- if (quiet)
- return 0;
+ if (forks && workload_exec_errno) {
+ char msg[512];
+ const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg));
+ pr_err("Workload failed: %s\n", emsg);
+ err = -1;
+ goto out_child;
+ }
- fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
+ if (!quiet) {
+ fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
+
+ /*
+ * Approximate RIP event size: 24 bytes.
+ */
+ fprintf(stderr,
+ "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n",
+ (double)rec->bytes_written / 1024.0 / 1024.0,
+ file->path,
+ rec->bytes_written / 24);
+ }
+
+out_child:
+ if (forks) {
+ int exit_status;
+
+ if (!child_finished)
+ kill(rec->evlist->workload.pid, SIGTERM);
+
+ wait(&exit_status);
+
+ if (err < 0)
+ status = err;
+ else if (WIFEXITED(exit_status))
+ status = WEXITSTATUS(exit_status);
+ else if (WIFSIGNALED(exit_status))
+ signr = WTERMSIG(exit_status);
+ } else
+ status = err;
+
+ if (!err && !file->is_pipe) {
+ rec->session->header.data_size += rec->bytes_written;
+
+ if (!rec->no_buildid)
+ process_buildids(rec);
+ perf_session__write_header(rec->session, rec->evlist,
+ file->fd, true);
+ }
+
+out_delete_session:
+ perf_session__delete(session);
+ return status;
+}
+
+#define BRANCH_OPT(n, m) \
+ { .name = n, .mode = (m) }
+
+#define BRANCH_END { .name = NULL }
+
+struct branch_mode {
+ const char *name;
+ int mode;
+};
+
+static const struct branch_mode branch_modes[] = {
+ BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER),
+ BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL),
+ BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV),
+ BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY),
+ BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL),
+ BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN),
+ BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL),
+ BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX),
+ BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX),
+ BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX),
+ BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),
+ BRANCH_END
+};
+
+static int
+parse_branch_stack(const struct option *opt, const char *str, int unset)
+{
+#define ONLY_PLM \
+ (PERF_SAMPLE_BRANCH_USER |\
+ PERF_SAMPLE_BRANCH_KERNEL |\
+ PERF_SAMPLE_BRANCH_HV)
+
+ uint64_t *mode = (uint64_t *)opt->value;
+ const struct branch_mode *br;
+ char *s, *os = NULL, *p;
+ int ret = -1;
+
+ if (unset)
+ return 0;
/*
- * Approximate RIP event size: 24 bytes.
+ * cannot set it twice, -b + --branch-filter for instance
*/
- fprintf(stderr,
- "[ perf record: Captured and wrote %.3f MB %s (~%lld samples) ]\n",
- (double)bytes_written / 1024.0 / 1024.0,
- output_name,
- bytes_written / 24);
+ if (*mode)
+ return -1;
+
+ /* str may be NULL in case no arg is passed to -b */
+ if (str) {
+ /* because str is read-only */
+ s = os = strdup(str);
+ if (!s)
+ return -1;
+
+ for (;;) {
+ p = strchr(s, ',');
+ if (p)
+ *p = '\0';
+
+ for (br = branch_modes; br->name; br++) {
+ if (!strcasecmp(s, br->name))
+ break;
+ }
+ if (!br->name) {
+ ui__warning("unknown branch filter %s,"
+ " check man page\n", s);
+ goto error;
+ }
+
+ *mode |= br->mode;
+
+ if (!p)
+ break;
+
+ s = p + 1;
+ }
+ }
+ ret = 0;
+ /* default to any branch */
+ if ((*mode & ~ONLY_PLM) == 0) {
+ *mode = PERF_SAMPLE_BRANCH_ANY;
+ }
+error:
+ free(os);
+ return ret;
+}
+
+#ifdef HAVE_DWARF_UNWIND_SUPPORT
+static int get_stack_size(char *str, unsigned long *_size)
+{
+ char *endptr;
+ unsigned long size;
+ unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
+
+ size = strtoul(str, &endptr, 0);
+
+ do {
+ if (*endptr)
+ break;
+
+ size = round_up(size, sizeof(u64));
+ if (!size || size > max_size)
+ break;
+
+ *_size = size;
+ return 0;
+
+ } while (0);
+
+ pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
+ max_size, str);
+ return -1;
+}
+#endif /* HAVE_DWARF_UNWIND_SUPPORT */
+
+int record_parse_callchain(const char *arg, struct record_opts *opts)
+{
+ char *tok, *name, *saveptr = NULL;
+ char *buf;
+ int ret = -1;
+
+ /* We need buffer that we know we can write to. */
+ buf = malloc(strlen(arg) + 1);
+ if (!buf)
+ return -ENOMEM;
+
+ strcpy(buf, arg);
+
+ tok = strtok_r((char *)buf, ",", &saveptr);
+ name = tok ? : (char *)buf;
+
+ do {
+ /* Framepointer style */
+ if (!strncmp(name, "fp", sizeof("fp"))) {
+ if (!strtok_r(NULL, ",", &saveptr)) {
+ opts->call_graph = CALLCHAIN_FP;
+ ret = 0;
+ } else
+ pr_err("callchain: No more arguments "
+ "needed for -g fp\n");
+ break;
+
+#ifdef HAVE_DWARF_UNWIND_SUPPORT
+ /* Dwarf style */
+ } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
+ const unsigned long default_stack_dump_size = 8192;
+
+ ret = 0;
+ opts->call_graph = CALLCHAIN_DWARF;
+ opts->stack_dump_size = default_stack_dump_size;
+
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (tok) {
+ unsigned long size = 0;
+
+ ret = get_stack_size(tok, &size);
+ opts->stack_dump_size = size;
+ }
+#endif /* HAVE_DWARF_UNWIND_SUPPORT */
+ } else {
+ pr_err("callchain: Unknown --call-graph option "
+ "value: %s\n", arg);
+ break;
+ }
+
+ } while (0);
+
+ free(buf);
+ return ret;
+}
+
+static void callchain_debug(struct record_opts *opts)
+{
+ static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" };
+
+ pr_debug("callchain: type %s\n", str[opts->call_graph]);
+
+ if (opts->call_graph == CALLCHAIN_DWARF)
+ pr_debug("callchain: stack dump size %d\n",
+ opts->stack_dump_size);
+}
+
+int record_parse_callchain_opt(const struct option *opt,
+ const char *arg,
+ int unset)
+{
+ struct record_opts *opts = opt->value;
+ int ret;
+
+ opts->call_graph_enabled = !unset;
+
+ /* --no-call-graph */
+ if (unset) {
+ opts->call_graph = CALLCHAIN_NONE;
+ pr_debug("callchain: disabled\n");
+ return 0;
+ }
+
+ ret = record_parse_callchain(arg, opts);
+ if (!ret)
+ callchain_debug(opts);
+
+ return ret;
+}
+
+int record_callchain_opt(const struct option *opt,
+ const char *arg __maybe_unused,
+ int unset __maybe_unused)
+{
+ struct record_opts *opts = opt->value;
+
+ opts->call_graph_enabled = !unset;
+
+ if (opts->call_graph == CALLCHAIN_NONE)
+ opts->call_graph = CALLCHAIN_FP;
+
+ callchain_debug(opts);
return 0;
+}
-out_delete_session:
- perf_session__delete(session);
- return err;
+static int perf_record_config(const char *var, const char *value, void *cb)
+{
+ struct record *rec = cb;
+
+ if (!strcmp(var, "record.call-graph"))
+ return record_parse_callchain(value, &rec->opts);
+
+ return perf_default_config(var, value, cb);
}
static const char * const record_usage[] = {
@@ -788,142 +764,197 @@ static const char * const record_usage[] = {
NULL
};
-static bool force, append_file;
+/*
+ * XXX Ideally would be local to cmd_record() and passed to a record__new
+ * because we need to have access to it in record__exit, that is called
+ * after cmd_record() exits, but since record_options need to be accessible to
+ * builtin-script, leave it here.
+ *
+ * At least we don't ouch it in all the other functions here directly.
+ *
+ * Just say no to tons of global variables, sigh.
+ */
+static struct record record = {
+ .opts = {
+ .mmap_pages = UINT_MAX,
+ .user_freq = UINT_MAX,
+ .user_interval = ULLONG_MAX,
+ .freq = 4000,
+ .target = {
+ .uses_mmap = true,
+ .default_per_cpu = true,
+ },
+ },
+};
+
+#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: "
+
+#ifdef HAVE_DWARF_UNWIND_SUPPORT
+const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf";
+#else
+const char record_callchain_help[] = CALLCHAIN_HELP "fp";
+#endif
+/*
+ * XXX Will stay a global variable till we fix builtin-script.c to stop messing
+ * with it and switch to use the library functions in perf_evlist that came
+ * from builtin-record.c, i.e. use record_opts,
+ * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record',
+ * using pipes, etc.
+ */
const struct option record_options[] = {
- OPT_CALLBACK('e', "event", NULL, "event",
+ OPT_CALLBACK('e', "event", &record.evlist, "event",
"event selector. use 'perf list' to list available events",
- parse_events),
- OPT_CALLBACK(0, "filter", NULL, "filter",
+ parse_events_option),
+ OPT_CALLBACK(0, "filter", &record.evlist, "filter",
"event filter", parse_filter),
- OPT_INTEGER('p', "pid", &target_pid,
+ OPT_STRING('p', "pid", &record.opts.target.pid, "pid",
"record events on existing process id"),
- OPT_INTEGER('t', "tid", &target_tid,
+ OPT_STRING('t', "tid", &record.opts.target.tid, "tid",
"record events on existing thread id"),
- OPT_INTEGER('r', "realtime", &realtime_prio,
+ OPT_INTEGER('r', "realtime", &record.realtime_prio,
"collect data with this RT SCHED_FIFO priority"),
- OPT_BOOLEAN('R', "raw-samples", &raw_samples,
+ OPT_BOOLEAN(0, "no-buffering", &record.opts.no_buffering,
+ "collect data without buffering"),
+ OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples,
"collect raw sample records from all opened counters"),
- OPT_BOOLEAN('a', "all-cpus", &system_wide,
+ OPT_BOOLEAN('a', "all-cpus", &record.opts.target.system_wide,
"system-wide collection from all CPUs"),
- OPT_BOOLEAN('A', "append", &append_file,
- "append to the output file to do incremental profiling"),
- OPT_STRING('C', "cpu", &cpu_list, "cpu",
+ OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu",
"list of cpus to monitor"),
- OPT_BOOLEAN('f', "force", &force,
- "overwrite existing data file (deprecated)"),
- OPT_U64('c', "count", &user_interval, "event period to sample"),
- OPT_STRING('o', "output", &output_name, "file",
+ OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"),
+ OPT_STRING('o', "output", &record.file.path, "file",
"output file name"),
- OPT_BOOLEAN('i', "no-inherit", &no_inherit,
- "child tasks do not inherit counters"),
- OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"),
- OPT_UINTEGER('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_SET('i', "no-inherit", &record.opts.no_inherit,
+ &record.opts.no_inherit_set,
+ "child tasks do not inherit counters"),
+ OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"),
+ OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages",
+ "number of mmap data pages",
+ perf_evlist__parse_mmap_pages),
+ OPT_BOOLEAN(0, "group", &record.opts.group,
+ "put the counters into a counter group"),
+ OPT_CALLBACK_NOOPT('g', NULL, &record.opts,
+ NULL, "enables call-graph recording" ,
+ &record_callchain_opt),
+ OPT_CALLBACK(0, "call-graph", &record.opts,
+ "mode[,dump_size]", record_callchain_help,
+ &record_parse_callchain_opt),
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"),
OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
- OPT_BOOLEAN('s', "stat", &inherit_stat,
+ OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat,
"per thread counts"),
- OPT_BOOLEAN('d', "data", &sample_address,
+ OPT_BOOLEAN('d', "data", &record.opts.sample_address,
"Sample addresses"),
- OPT_BOOLEAN('n', "no-samples", &no_samples,
+ OPT_BOOLEAN('T', "timestamp", &record.opts.sample_time, "Sample timestamps"),
+ OPT_BOOLEAN('P', "period", &record.opts.period, "Sample period"),
+ OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples,
"don't sample"),
- OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid,
+ OPT_BOOLEAN('N', "no-buildid-cache", &record.no_buildid_cache,
"do not update the buildid cache"),
+ OPT_BOOLEAN('B', "no-buildid", &record.no_buildid,
+ "do not collect buildids in perf.data"),
+ OPT_CALLBACK('G', "cgroup", &record.evlist, "name",
+ "monitor event in cgroup name only",
+ parse_cgroups),
+ OPT_UINTEGER('D', "delay", &record.opts.initial_delay,
+ "ms to wait before starting measurement after program start"),
+ OPT_STRING('u', "uid", &record.opts.target.uid_str, "user",
+ "user to profile"),
+
+ OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack,
+ "branch any", "sample any taken branches",
+ parse_branch_stack),
+
+ OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack,
+ "branch filter mask", "branch stack filter modes",
+ parse_branch_stack),
+ OPT_BOOLEAN('W', "weight", &record.opts.sample_weight,
+ "sample by weight (on special events only)"),
+ OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction,
+ "sample transaction flags (special events only)"),
+ OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread,
+ "use per-thread mmaps"),
OPT_END()
};
-int cmd_record(int argc, const char **argv, const char *prefix __used)
+int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
{
- int i, j, err = -ENOMEM;
+ int err = -ENOMEM;
+ struct record *rec = &record;
+ char errbuf[BUFSIZ];
+
+ rec->evlist = perf_evlist__new();
+ if (rec->evlist == NULL)
+ return -ENOMEM;
+
+ perf_config(perf_record_config, rec);
argc = parse_options(argc, argv, record_options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
- if (!argc && target_pid == -1 && target_tid == -1 &&
- !system_wide && !cpu_list)
+ if (!argc && target__none(&rec->opts.target))
usage_with_options(record_usage, record_options);
- if (force && append_file) {
- fprintf(stderr, "Can't overwrite and append at the same time."
- " You need to choose between -f and -A");
+ if (nr_cgroups && !rec->opts.target.system_wide) {
+ ui__error("cgroup monitoring only available in"
+ " system-wide mode\n");
usage_with_options(record_usage, record_options);
- } else if (append_file) {
- write_mode = WRITE_APPEND;
- } else {
- write_mode = WRITE_FORCE;
}
symbol__init();
- if (no_buildid)
+
+ if (symbol_conf.kptr_restrict)
+ pr_warning(
+"WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,\n"
+"check /proc/sys/kernel/kptr_restrict.\n\n"
+"Samples in kernel functions may not be resolved if a suitable vmlinux\n"
+"file is not found in the buildid cache or in the vmlinux path.\n\n"
+"Samples in kernel modules won't be resolved at all.\n\n"
+"If some relocation was applied (e.g. kexec) symbols may be misresolved\n"
+"even with a suitable vmlinux or kallsyms file.\n\n");
+
+ if (rec->no_buildid_cache || rec->no_buildid)
disable_buildid_cache();
- if (!nr_counters) {
- nr_counters = 1;
- attrs[0].type = PERF_TYPE_HARDWARE;
- attrs[0].config = PERF_COUNT_HW_CPU_CYCLES;
+ if (rec->evlist->nr_entries == 0 &&
+ perf_evlist__add_default(rec->evlist) < 0) {
+ pr_err("Not enough memory for event selector list\n");
+ goto out_symbol_exit;
}
- 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, record_options);
- }
- } else {
- all_tids=malloc(sizeof(pid_t));
- if (!all_tids)
- goto out_symbol_exit;
+ if (rec->opts.target.tid && !rec->opts.no_inherit_set)
+ rec->opts.no_inherit = true;
- all_tids[0] = target_tid;
- thread_num = 1;
+ err = target__validate(&rec->opts.target);
+ if (err) {
+ target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
+ ui__warning("%s", errbuf);
}
- for (i = 0; i < MAX_NR_CPUS; i++) {
- for (j = 0; j < MAX_COUNTERS; j++) {
- fd[i][j] = malloc(sizeof(int)*thread_num);
- if (!fd[i][j])
- goto out_free_fd;
- }
- }
- event_array = malloc(
- sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num);
- if (!event_array)
- goto out_free_fd;
+ err = target__parse_uid(&rec->opts.target);
+ if (err) {
+ int saved_errno = errno;
- if (user_interval != ULLONG_MAX)
- default_interval = user_interval;
- if (user_freq != UINT_MAX)
- freq = user_freq;
+ target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
+ ui__error("%s", errbuf);
- /*
- * 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");
- err = -EINVAL;
- goto out_free_event_array;
+ err = -saved_errno;
+ goto out_symbol_exit;
}
- err = __cmd_record(argc, argv);
+ err = -ENOMEM;
+ if (perf_evlist__create_maps(rec->evlist, &rec->opts.target) < 0)
+ usage_with_options(record_usage, record_options);
-out_free_event_array:
- free(event_array);
-out_free_fd:
- for (i = 0; i < MAX_NR_CPUS; i++) {
- for (j = 0; j < MAX_COUNTERS; j++)
- free(fd[i][j]);
+ if (record_opts__config(&rec->opts)) {
+ err = -EINVAL;
+ goto out_symbol_exit;
}
- free(all_tids);
- all_tids = NULL;
+
+ err = __cmd_record(&record, argc, argv);
out_symbol_exit:
+ perf_evlist__delete(rec->evlist);
symbol__exit();
return err;
}