|
|
@@ -59,6 +59,7 @@
|
|
|
#include "util/thread.h"
|
|
|
#include "util/thread_map.h"
|
|
|
#include "util/counts.h"
|
|
|
+#include "util/session.h"
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
#include <sys/prctl.h>
|
|
|
@@ -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 [<options>]",
|
|
|
+ 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:
|