#include <unistd.h>
#include <sched.h>
+enum write_mode_t {
+ WRITE_FORCE,
+ WRITE_APPEND
+};
+
static int *fd[MAX_NR_CPUS][MAX_COUNTERS];
+static unsigned int user_interval = UINT_MAX;
static long 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 unsigned int realtime_prio = 0;
static pid_t *all_tids = NULL;
static int thread_num = 0;
static pid_t child_pid = -1;
-static bool inherit = true;
-static bool force = false;
-static bool append_file = false;
+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;
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) {
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)
attr->mmap = track;
attr->comm = track;
- attr->inherit = inherit;
- if (target_pid == -1 && !system_wide) {
+ attr->inherit = !no_inherit;
+ if (target_pid == -1 && target_tid == -1 && !system_wide) {
attr->disabled = 1;
attr->enable_on_exec = 1;
}
static void atexit_header(void)
{
- session->header.data_size += bytes_written;
+ if (!pipe_output) {
+ session->header.data_size += bytes_written;
- process_buildids();
- perf_header__write(&session->header, output, true);
+ process_buildids();
+ perf_header__write(&session->header, output, true);
+ }
+}
+
+static void event__synthesize_guest_os(struct machine *machine, void *data)
+{
+ int err;
+ char *guest_kallsyms;
+ char path[PATH_MAX];
+ struct perf_session *psession = data;
+
+ if (machine__is_host(machine))
+ return;
+
+ /*
+ *As for guest kernel when processing subcommand record&report,
+ *we arrange module mmap prior to guest kernel mmap and trigger
+ *a preload dso because default guest module symbols are loaded
+ *from guest kallsyms instead of /lib/modules/XXX/XXX. This
+ *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);
+ if (err < 0)
+ pr_err("Couldn't record guest kernel [%d]'s reference"
+ " relocation symbol.\n", machine->pid);
+
+ if (machine__is_default_guest(machine))
+ guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms;
+ else {
+ sprintf(path, "%s/proc/kallsyms", machine->root_dir);
+ guest_kallsyms = path;
+ }
+
+ /*
+ * 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");
+ if (err < 0)
+ pr_err("Couldn't record guest kernel [%d]'s reference"
+ " relocation symbol.\n", machine->pid);
+}
+
+static struct perf_event_header finished_round_event = {
+ .size = sizeof(struct perf_event_header),
+ .type = PERF_RECORD_FINISHED_ROUND,
+};
+
+static void mmap_read_all(void)
+{
+ int i, counter, thread;
+
+ for (i = 0; i < nr_cpu; i++) {
+ 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 (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
+ write_output(&finished_round_event, sizeof(finished_round_event));
}
static int __cmd_record(int argc, const char **argv)
int child_ready_pipe[2], go_pipe[2];
const bool forks = argc > 0;
char buf;
+ struct machine *machine;
page_size = sysconf(_SC_PAGE_SIZE);
exit(-1);
}
- if (!stat(output_name, &st) && st.st_size) {
- 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 {
+ 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 = false;
+ } 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);
}
- session = perf_session__new(output_name, O_WRONLY, force);
+ session = perf_session__new(output_name, O_WRONLY,
+ write_mode == WRITE_FORCE, false);
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);
+ err = perf_header__read(session, output);
if (err < 0)
return err;
}
- if (raw_samples) {
+ if (have_tracepoints(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) {
- perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
- break;
- }
- }
- }
atexit(atexit_header);
}
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);
close(child_ready_pipe[0]);
}
- if ((!system_wide && !inherit) || profile_cpu != -1) {
+ if ((!system_wide && no_inherit) || profile_cpu != -1) {
open_counters(profile_cpu);
} else {
nr_cpus = read_cpu_map();
open_counters(cpumap[i]);
}
- if (file_new) {
+ 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;
+ }
+
+ if (have_tracepoints(attrs, nr_counters)) {
+ /*
+ * FIXME err <= 0 here actually means that
+ * there were no tracepoints so its not really
+ * an error, just that we don't need to
+ * synthesize anything. We really have to
+ * 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);
+ if (err <= 0) {
+ pr_err("Couldn't record tracing data.\n");
+ return err;
+ }
+ advance_output(err);
+ }
+ }
+
+ machine = perf_session__find_host_machine(session);
+ if (!machine) {
+ pr_err("Couldn't find native kernel information.\n");
+ return -1;
+ }
+
err = event__synthesize_kernel_mmap(process_synthesized_event,
- session, "_text");
+ session, machine, "_text");
if (err < 0)
err = event__synthesize_kernel_mmap(process_synthesized_event,
- session, "_stext");
+ session, machine, "_stext");
if (err < 0) {
pr_err("Couldn't record kernel reference relocation symbol.\n");
return err;
}
- err = event__synthesize_modules(process_synthesized_event, session);
+ 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)
+ perf_session__process_machines(session, event__synthesize_guest_os);
if (!system_wide && profile_cpu == -1)
event__synthesize_thread(target_tid, process_synthesized_event,
int hits = samples;
int thread;
- for (i = 0; i < nr_cpu; i++) {
- 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]);
- }
-
- }
- }
+ mmap_read_all();
if (hits == samples) {
if (done)
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",
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_BOOLEAN('i', "no-inherit", &no_inherit,
+ "child tasks do not inherit counters"),
+ OPT_INTEGER('F', "freq", &user_freq,
"profile at this frequency"),
OPT_INTEGER('m', "mmap-pages", &mmap_pages,
"number of mmap data pages"),
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,
!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) {
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.
*/
exit(EXIT_FAILURE);
}
- for (counter = 0; counter < nr_counters; counter++) {
- if (attrs[counter].sample_period)
- continue;
-
- attrs[counter].sample_period = default_interval;
- }
-
return __cmd_record(argc, argv);
}