summaryrefslogtreecommitdiff
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.c201
1 files changed, 122 insertions, 79 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 7c8020a..d046514 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -24,13 +24,12 @@
#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>
-#ifndef HAVE_ON_EXIT_SUPPORT
+#ifndef HAVE_ON_EXIT
#ifndef ATEXIT_MAX
#define ATEXIT_MAX 32
#endif
@@ -66,25 +65,31 @@ struct perf_record {
struct perf_tool tool;
struct perf_record_opts opts;
u64 bytes_written;
- struct perf_data_file file;
+ const char *output_name;
struct perf_evlist *evlist;
struct perf_session *session;
const char *progname;
+ int output;
+ unsigned int page_size;
int realtime_prio;
bool no_buildid;
bool no_buildid_cache;
long samples;
+ off_t post_processing_offset;
};
-static int do_write_output(struct perf_record *rec, void *buf, size_t size)
+static void advance_output(struct perf_record *rec, size_t size)
{
- struct perf_data_file *file = &rec->file;
+ rec->bytes_written += size;
+}
+static int write_output(struct perf_record *rec, void *buf, size_t size)
+{
while (size) {
- ssize_t ret = write(file->fd, buf, size);
+ int ret = write(rec->output, buf, size);
if (ret < 0) {
- pr_err("failed to write perf data, error: %m\n");
+ pr_err("failed to write\n");
return -1;
}
@@ -97,11 +102,6 @@ static int do_write_output(struct perf_record *rec, void *buf, size_t size)
return 0;
}
-static int write_output(struct perf_record *rec, void *buf, size_t size)
-{
- return do_write_output(rec, buf, size);
-}
-
static int process_synthesized_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
@@ -119,7 +119,7 @@ static int perf_record__mmap_read(struct perf_record *rec,
{
unsigned int head = perf_mmap__read_head(md);
unsigned int old = md->prev;
- unsigned char *data = md->base + page_size;
+ unsigned char *data = md->base + rec->page_size;
unsigned long size;
void *buf;
int rc = 0;
@@ -234,6 +234,10 @@ try_again:
"or try again with a smaller value of -m/--mmap_pages.\n"
"(current value: %d)\n", opts->mmap_pages);
rc = -errno;
+ } else if (!is_power_of_2(opts->mmap_pages) &&
+ (opts->mmap_pages != UINT_MAX)) {
+ pr_err("--mmap_pages/-m value must be a power of two.");
+ rc = -EINVAL;
} else {
pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno));
rc = -errno;
@@ -249,34 +253,31 @@ out:
static int process_buildids(struct perf_record *rec)
{
- struct perf_data_file *file = &rec->file;
- struct perf_session *session = rec->session;
- u64 start = session->header.data_offset;
+ u64 size = lseek(rec->output, 0, SEEK_CUR);
- u64 size = lseek(file->fd, 0, SEEK_CUR);
if (size == 0)
return 0;
- return __perf_session__process_events(session, start,
- size - start,
+ rec->session->fd = rec->output;
+ return __perf_session__process_events(rec->session, rec->post_processing_offset,
+ size - rec->post_processing_offset,
size, &build_id__mark_dso_hit_ops);
}
static void perf_record__exit(int status, void *arg)
{
struct perf_record *rec = arg;
- struct perf_data_file *file = &rec->file;
if (status != 0)
return;
- if (!file->is_pipe) {
+ if (!rec->opts.pipe_output) {
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);
+ rec->output, true);
perf_session__delete(rec->session);
perf_evlist__delete(rec->evlist);
symbol__exit();
@@ -342,47 +343,64 @@ out:
return rc;
}
-static void perf_record__init_features(struct perf_record *rec)
-{
- struct perf_evlist *evsel_list = rec->evlist;
- 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(&evsel_list->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 int __cmd_record(struct perf_record *rec, int argc, const char **argv)
{
- int err;
+ struct stat st;
+ int flags;
+ int err, output, feat;
unsigned long waking = 0;
const bool forks = argc > 0;
struct machine *machine;
struct perf_tool *tool = &rec->tool;
struct perf_record_opts *opts = &rec->opts;
struct perf_evlist *evsel_list = rec->evlist;
- struct perf_data_file *file = &rec->file;
+ const char *output_name = rec->output_name;
struct perf_session *session;
bool disabled = false;
rec->progname = argv[0];
+ rec->page_size = sysconf(_SC_PAGE_SIZE);
+
on_exit(perf_record__sig_exit, rec);
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
signal(SIGUSR1, sig_handler);
signal(SIGTERM, sig_handler);
- session = perf_session__new(file, false, NULL);
+ if (!output_name) {
+ if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode))
+ opts->pipe_output = true;
+ else
+ rec->output_name = output_name = "perf.data";
+ }
+ if (output_name) {
+ if (!strcmp(output_name, "-"))
+ opts->pipe_output = true;
+ else if (!stat(output_name, &st) && st.st_size) {
+ char oldname[PATH_MAX];
+ snprintf(oldname, sizeof(oldname), "%s.old",
+ output_name);
+ unlink(oldname);
+ rename(output_name, oldname);
+ }
+ }
+
+ flags = O_CREAT|O_RDWR|O_TRUNC;
+
+ if (opts->pipe_output)
+ output = STDOUT_FILENO;
+ else
+ output = open(output_name, flags, S_IRUSR | S_IWUSR);
+ if (output < 0) {
+ perror("failed to create output file");
+ return -1;
+ }
+
+ rec->output = output;
+
+ session = perf_session__new(output_name, O_WRONLY,
+ true, false, NULL);
if (session == NULL) {
pr_err("Not enough memory for reading perf file header\n");
return -1;
@@ -390,11 +408,21 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
rec->session = session;
- perf_record__init_features(rec);
+ 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(&evsel_list->entries))
+ perf_header__clear_feat(&session->header, HEADER_TRACING_DATA);
+
+ if (!rec->opts.branch_stack)
+ perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
if (forks) {
err = perf_evlist__prepare_workload(evsel_list, &opts->target,
- argv, file->is_pipe,
+ argv, opts->pipe_output,
true);
if (err < 0) {
pr_err("Couldn't run the workload!\n");
@@ -415,13 +443,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
*/
on_exit(perf_record__exit, rec);
- if (file->is_pipe) {
- err = perf_header__write_pipe(file->fd);
+ if (opts->pipe_output) {
+ err = perf_header__write_pipe(output);
if (err < 0)
goto out_delete_session;
} else {
err = perf_session__write_header(session, evsel_list,
- file->fd, false);
+ output, false);
if (err < 0)
goto out_delete_session;
}
@@ -434,9 +462,11 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
goto out_delete_session;
}
+ rec->post_processing_offset = lseek(output, 0, SEEK_CUR);
+
machine = &session->machines.host;
- if (file->is_pipe) {
+ if (opts->pipe_output) {
err = perf_event__synthesize_attrs(tool, session,
process_synthesized_event);
if (err < 0) {
@@ -453,13 +483,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
* return this more properly and also
* propagate errors that now are calling die()
*/
- err = perf_event__synthesize_tracing_data(tool, file->fd, evsel_list,
+ err = perf_event__synthesize_tracing_data(tool, output, evsel_list,
process_synthesized_event);
if (err <= 0) {
pr_err("Couldn't record tracing data.\n");
goto out_delete_session;
}
- rec->bytes_written += err;
+ advance_output(rec, err);
}
}
@@ -485,8 +515,16 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
perf_event__synthesize_guest_os, tool);
}
- err = __machine__synthesize_threads(machine, tool, &opts->target, evsel_list->threads,
- process_synthesized_event, opts->sample_address);
+ if (perf_target__has_task(&opts->target))
+ err = perf_event__synthesize_thread_map(tool, evsel_list->threads,
+ process_synthesized_event,
+ machine);
+ else if (perf_target__has_cpu(&opts->target))
+ err = perf_event__synthesize_threads(tool, process_synthesized_event,
+ machine);
+ else /* command specified */
+ err = 0;
+
if (err != 0)
goto out_delete_session;
@@ -506,7 +544,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
* (apart from group members) have enable_on_exec=1 set,
* so don't spoil it by prematurely enabling them.
*/
- if (!target__none(&opts->target))
+ if (!perf_target__none(&opts->target))
perf_evlist__enable(evsel_list);
/*
@@ -535,7 +573,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
* 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)) {
+ if (done && !disabled && !perf_target__none(&opts->target)) {
perf_evlist__disable(evsel_list);
disabled = true;
}
@@ -552,7 +590,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
fprintf(stderr,
"[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n",
(double)rec->bytes_written / 1024.0 / 1024.0,
- file->path,
+ output_name,
rec->bytes_written / 24);
return 0;
@@ -580,9 +618,6 @@ static const struct branch_mode branch_modes[] = {
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_END
};
@@ -649,7 +684,7 @@ error:
return ret;
}
-#ifdef HAVE_LIBUNWIND_SUPPORT
+#ifdef LIBUNWIND_SUPPORT
static int get_stack_size(char *str, unsigned long *_size)
{
char *endptr;
@@ -675,7 +710,7 @@ static int get_stack_size(char *str, unsigned long *_size)
max_size, str);
return -1;
}
-#endif /* HAVE_LIBUNWIND_SUPPORT */
+#endif /* LIBUNWIND_SUPPORT */
int record_parse_callchain(const char *arg, struct perf_record_opts *opts)
{
@@ -704,7 +739,7 @@ int record_parse_callchain(const char *arg, struct perf_record_opts *opts)
"needed for -g fp\n");
break;
-#ifdef HAVE_LIBUNWIND_SUPPORT
+#ifdef LIBUNWIND_SUPPORT
/* Dwarf style */
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
const unsigned long default_stack_dump_size = 8192;
@@ -720,7 +755,7 @@ int record_parse_callchain(const char *arg, struct perf_record_opts *opts)
ret = get_stack_size(tok, &size);
opts->stack_dump_size = size;
}
-#endif /* HAVE_LIBUNWIND_SUPPORT */
+#endif /* LIBUNWIND_SUPPORT */
} else {
pr_err("callchain: Unknown --call-graph option "
"value: %s\n", arg);
@@ -806,7 +841,7 @@ static struct perf_record record = {
#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: "
-#ifdef HAVE_LIBUNWIND_SUPPORT
+#ifdef LIBUNWIND_SUPPORT
const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf";
#else
const char record_callchain_help[] = CALLCHAIN_HELP "fp";
@@ -840,14 +875,13 @@ const struct option record_options[] = {
OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu",
"list of cpus to monitor"),
OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"),
- OPT_STRING('o', "output", &record.file.path, "file",
+ OPT_STRING('o', "output", &record.output_name, "file",
"output file name"),
OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit,
"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_UINTEGER('m', "mmap-pages", &record.opts.mmap_pages,
+ "number of mmap data pages"),
OPT_BOOLEAN(0, "group", &record.opts.group,
"put the counters into a counter group"),
OPT_CALLBACK_NOOPT('g', NULL, &record.opts,
@@ -886,10 +920,6 @@ const struct option record_options[] = {
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, "force-per-cpu", &record.opts.target.force_per_cpu,
- "force the use of per-cpu mmaps"),
OPT_END()
};
@@ -908,7 +938,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
argc = parse_options(argc, argv, record_options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
- if (!argc && target__none(&rec->opts.target))
+ if (!argc && perf_target__none(&rec->opts.target))
usage_with_options(record_usage, record_options);
if (nr_cgroups && !rec->opts.target.system_wide) {
@@ -938,17 +968,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
goto out_symbol_exit;
}
- err = target__validate(&rec->opts.target);
+ err = perf_target__validate(&rec->opts.target);
if (err) {
- target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
+ perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
ui__warning("%s", errbuf);
}
- err = target__parse_uid(&rec->opts.target);
+ err = perf_target__parse_uid(&rec->opts.target);
if (err) {
int saved_errno = errno;
- target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
+ perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
ui__error("%s", errbuf);
err = -saved_errno;
@@ -959,7 +989,20 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0)
usage_with_options(record_usage, record_options);
- if (perf_record_opts__config(&rec->opts)) {
+ if (rec->opts.user_interval != ULLONG_MAX)
+ rec->opts.default_interval = rec->opts.user_interval;
+ if (rec->opts.user_freq != UINT_MAX)
+ rec->opts.freq = rec->opts.user_freq;
+
+ /*
+ * User specified count overrides default frequency.
+ */
+ if (rec->opts.default_interval)
+ rec->opts.freq = 0;
+ else if (rec->opts.freq) {
+ rec->opts.default_interval = rec->opts.freq;
+ } else {
+ ui__error("frequency and count are zero, aborting\n");
err = -EINVAL;
goto out_free_fd;
}