diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 4e074a6608269793d52a2ea4f28061532bb9798b..70eee1c2c444e762c75405edfe9ee93e768416f0 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -10,6 +10,7 @@ SYNOPSIS [verse] 'perf stat' [-e | --event=EVENT] [-a] 'perf stat' [-e | --event=EVENT] [-a] -- [] +'perf stat' [-e | --event=EVENT] [-a] record [-o file] -- [] DESCRIPTION ----------- @@ -22,6 +23,8 @@ OPTIONS ...:: Any command you can specify in a shell. +record:: + See STAT RECORD. -e:: --event=:: @@ -159,6 +162,15 @@ filter out the startup phase of the program, which is often very different. Print statistics of transactional execution if supported. +STAT RECORD +----------- +Stores stat data into perf data file. + +-o file:: +--output file:: +Output file name. + + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index bbf42eefd5e5b708f419e8c3e24a7d1f8aaf9dde..af2a3bf659f76e096ca53a09f922cc3e4cf77f5e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -59,6 +59,7 @@ #include "util/thread.h" #include "util/thread_map.h" #include "util/counts.h" +#include "util/session.h" #include #include @@ -126,6 +127,16 @@ static bool append_file; static const char *output_name; static int output_fd; +struct perf_stat { + bool record; + struct perf_data_file file; + struct perf_session *session; + u64 bytes_written; +}; + +static struct perf_stat perf_stat; +#define STAT_RECORD perf_stat.record + static volatile int done = 0; static struct perf_stat_config stat_config = { @@ -166,7 +177,11 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) * like tracepoints. Clear it up for counting. */ attr->sample_period = 0; - attr->sample_type = 0; + /* + * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless + * while avoiding that older tools show confusing messages. + */ + attr->sample_type = PERF_SAMPLE_IDENTIFIER; /* * Disabling all counters initially, they will be enabled @@ -202,6 +217,26 @@ static inline int nsec_counter(struct perf_evsel *evsel) return 0; } +static int perf_stat__write(struct perf_stat *stat, void *bf, size_t size) +{ + if (perf_data_file__write(stat->session->file, bf, size) < 0) { + pr_err("failed to write perf data, error: %m\n"); + return -1; + } + + stat->bytes_written += size; + return 0; +} + +static int process_synthesized_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct perf_stat *stat = (void *)tool; + return perf_stat__write(stat, event, event->header.size); +} + /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode @@ -361,6 +396,15 @@ static int __run_perf_stat(int argc, const char **argv) return -1; } + if (STAT_RECORD) { + int err, fd = perf_data_file__fd(&perf_stat.file); + + err = perf_session__write_header(perf_stat.session, evsel_list, + fd, false); + if (err < 0) + return err; + } + /* * Enable counters and exec the command: */ @@ -1261,6 +1305,38 @@ static int add_default_attributes(void) return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs); } +static const char * const recort_usage[] = { + "perf stat record []", + NULL, +}; + +static int __cmd_record(int argc, const char **argv) +{ + struct perf_session *session; + struct perf_data_file *file = &perf_stat.file; + + argc = parse_options(argc, argv, stat_options, record_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (output_name) + file->path = output_name; + + session = perf_session__new(file, false, NULL); + if (session == NULL) { + pr_err("Perf session creation failed.\n"); + return -1; + } + + /* No pipe support ATM */ + if (perf_stat.file.is_pipe) + return -EINVAL; + + session->evlist = evsel_list; + perf_stat.session = session; + perf_stat.record = true; + return argc; +} + int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const stat_usage[] = { @@ -1271,6 +1347,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) const char *mode; FILE *output = stderr; unsigned int interval; + const char * const stat_subcommands[] = { "record" }; setlocale(LC_ALL, ""); @@ -1278,12 +1355,22 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (evsel_list == NULL) return -ENOMEM; - argc = parse_options(argc, argv, stat_options, stat_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, + (const char **) stat_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (argc && !strncmp(argv[0], "rec", 3)) { + argc = __cmd_record(argc, argv); + if (argc < 0) + return -1; + } interval = stat_config.interval; - if (output_name && strcmp(output_name, "-")) + /* + * For record command the -o is already taken care of. + */ + if (!STAT_RECORD && output_name && strcmp(output_name, "-")) output = NULL; if (output_name && output_fd) { @@ -1450,6 +1537,31 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (!forever && status != -1 && !interval) print_counters(NULL, argc, argv); + if (STAT_RECORD) { + /* + * We synthesize the kernel mmap record just so that older tools + * don't emit warnings about not being able to resolve symbols + * due to /proc/sys/kernel/kptr_restrict settings and instear provide + * a saner message about no samples being in the perf.data file. + * + * This also serves to suppress a warning about f_header.data.size == 0 + * in header.c. -acme + */ + int fd = perf_data_file__fd(&perf_stat.file); + int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat, + process_synthesized_event, + &perf_stat.session->machines.host); + if (err) { + pr_warning("Couldn't synthesize the kernel mmap record, harmless, " + "older tools may produce warnings about this file\n."); + } + + perf_stat.session->header.data_size += perf_stat.bytes_written; + perf_session__write_header(perf_stat.session, evsel_list, fd, true); + + perf_session__delete(perf_stat.session); + } + perf_stat__exit_aggr_mode(); perf_evlist__free_stats(evsel_list); out: