[PATCH 02/25] perf stat record: Add record command

Jiri Olsa
2015-11-05T14:41:21Z

Add 'perf stat record' command support. It creates simple
(header only) perf.data file ATM.

The record command could be specified anywhere among stat
options. All stat command options are valid for stat record
command with '-o' option exception. If specified for record
command it denotes the perf data file name.

Tested-by: Kan Liang <kan.liang@intel.com>
Link: http://lkml.kernel.org/n/tip-0av5yfkwyywwgoiali88w4hi@git.kernel.org
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
tools/perf/Documentation/perf-stat.txt | 12 ++++++
tools/perf/builtin-stat.c | 78 ++++++++++++++++++++++++++++++++--
2 files changed, 87 insertions(+), 3 deletions(-)

diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 4e074a660826..70eee1c2c444 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=EVENT] [-a] <command>
'perf stat' [-e <EVENT> | --event=EVENT] [-a] -- <command> [<options>]
+'perf stat' [-e <EVENT> | --event=EVENT] [-a] record [-o file] -- <command> [<options>]

DESCRIPTION
-----------
@@ -22,6 +23,8 @@ OPTIONS
<command>...::
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 e77880b5094d..04123835fd81 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 <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 = {
@@ -344,6 +355,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:
*/
@@ -1236,6 +1256,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[] = {
@@ -1246,6 +1298,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, "");

@@ -1253,12 +1306,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) {
@@ -1425,6 +1488,15 @@ 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) {
+ int fd = perf_data_file__fd(&perf_stat.file);
+
+ 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_evlist__free_stats(evsel_list);
out:
perf_evlist__delete(evsel_list);
--
2.4.3