A new argument --format is added to specify an alternate output
format. If perf is compiled with libbabeltrace, support for the
ctf format is available. An example:

perf record --format ctf -e sched:sched_switch ls

Signed-off-by: John Ogness <[email protected]>
---
Patch against next-20151215.

There definately could be some beautification so the output
is more useful. But I am interested if this approach is
generally how we should do it.

 tools/perf/builtin-record.c        |   86 ++++++++++++++---
 tools/perf/util/data-convert-bt.c  |  186 +++++++++++++++++++++++++++++++++++-
 tools/perf/util/format-converter.h |   17 ++++
 3 files changed, 272 insertions(+), 17 deletions(-)
 create mode 100644 tools/perf/util/format-converter.h

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 199fc31..49f555c 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -32,6 +32,7 @@
 #include "util/parse-branch-options.h"
 #include "util/parse-regs-options.h"
 #include "util/llvm-utils.h"
+#include "util/format-converter.h"
 
 #include <unistd.h>
 #include <sched.h>
@@ -41,6 +42,7 @@
 struct record {
        struct perf_tool        tool;
        struct record_opts      opts;
+       struct format_converter *format;
        u64                     bytes_written;
        struct perf_data_file   file;
        struct auxtrace_record  *itr;
@@ -53,9 +55,18 @@ struct record {
        unsigned long long      samples;
 };
 
+
 static int record__write(struct record *rec, void *bf, size_t size)
 {
-       if (perf_data_file__write(rec->session->file, bf, size) < 0) {
+       if (rec->format) {
+               if (rec->format->write &&
+                   rec->format->write(rec->evlist, bf, size,
+                                      rec->format->priv)) {
+                       pr_err("failed to write alternate perf data format\n");
+                       return -1;
+               }
+
+       } else if (perf_data_file__write(rec->session->file, bf, size) < 0) {
                pr_err("failed to write perf data, error: %m\n");
                return -1;
        }
@@ -153,7 +164,7 @@ static int record__process_auxtrace(struct perf_tool *tool,
        size_t padding;
        u8 pad[8] = {0};
 
-       if (!perf_data_file__is_pipe(file)) {
+       if (!perf_data_file__is_pipe(file) && !rec->format) {
                off_t file_offset;
                int fd = perf_data_file__fd(file);
                int err;
@@ -497,7 +508,10 @@ static int __cmd_record(struct record *rec, int argc, 
const char **argv)
        else
                signal(SIGUSR2, SIG_IGN);
 
-       session = perf_session__new(file, false, tool);
+       if (rec->format)
+               session = perf_session__new(NULL, false, tool);
+       else
+               session = perf_session__new(file, false, tool);
        if (session == NULL) {
                pr_err("Perf session creation failed.\n");
                return -1;
@@ -536,7 +550,17 @@ static int __cmd_record(struct record *rec, int argc, 
const char **argv)
        if (!rec->evlist->nr_groups)
                perf_header__clear_feat(&session->header, HEADER_GROUP_DESC);
 
-       if (file->is_pipe) {
+       if (rec->format) {
+               if (!file->path)
+                       file->path = "perf.data";
+               if (rec->format->init &&
+                   rec->format->init(rec->evlist, file->path,
+                                     &rec->format->priv)) {
+                       pr_err("Failed to initialize alternate format.\n");
+                       err = -1;
+                       goto out_child;
+               }
+       } else if (file->is_pipe) {
                err = perf_header__write_pipe(fd);
                if (err < 0)
                        goto out_child;
@@ -561,7 +585,7 @@ static int __cmd_record(struct record *rec, int argc, const 
char **argv)
                                                   process_synthesized_event);
                if (err < 0) {
                        pr_err("Couldn't synthesize attrs.\n");
-                       goto out_child;
+                       goto out_format;
                }
 
                if (have_tracepoints(&rec->evlist->entries)) {
@@ -577,7 +601,7 @@ static int __cmd_record(struct record *rec, int argc, const 
char **argv)
                                                                  
process_synthesized_event);
                        if (err <= 0) {
                                pr_err("Couldn't record tracing data.\n");
-                               goto out_child;
+                               goto out_format;
                        }
                        rec->bytes_written += err;
                }
@@ -587,7 +611,7 @@ static int __cmd_record(struct record *rec, int argc, const 
char **argv)
                err = perf_event__synthesize_auxtrace_info(rec->itr, tool,
                                        session, process_synthesized_event);
                if (err)
-                       goto out_delete_session;
+                       goto out_format;
        }
 
        err = perf_event__synthesize_kernel_mmap(tool, 
process_synthesized_event,
@@ -613,7 +637,7 @@ static int __cmd_record(struct record *rec, int argc, const 
char **argv)
                                            process_synthesized_event, 
opts->sample_address,
                                            opts->proc_map_timeout);
        if (err != 0)
-               goto out_child;
+               goto out_format;
 
        if (rec->realtime_prio) {
                struct sched_param param;
@@ -622,7 +646,7 @@ static int __cmd_record(struct record *rec, int argc, const 
char **argv)
                if (sched_setscheduler(0, SCHED_FIFO, &param)) {
                        pr_err("Could not set realtime priority.\n");
                        err = -1;
-                       goto out_child;
+                       goto out_format;
                }
        }
 
@@ -673,7 +697,7 @@ static int __cmd_record(struct record *rec, int argc, const 
char **argv)
                if (record__mmap_read_all(rec) < 0) {
                        auxtrace_snapshot_enabled = 0;
                        err = -1;
-                       goto out_child;
+                       goto out_format;
                }
 
                if (auxtrace_record__snapshot_started) {
@@ -683,7 +707,7 @@ static int __cmd_record(struct record *rec, int argc, const 
char **argv)
                        if (auxtrace_snapshot_err) {
                                pr_err("AUX area tracing snapshot failed\n");
                                err = -1;
-                               goto out_child;
+                               goto out_format;
                        }
                }
 
@@ -721,12 +745,15 @@ static int __cmd_record(struct record *rec, int argc, 
const char **argv)
                const char *emsg = strerror_r(workload_exec_errno, msg, 
sizeof(msg));
                pr_err("Workload failed: %s\n", emsg);
                err = -1;
-               goto out_child;
+               goto out_format;
        }
 
        if (!quiet)
                fprintf(stderr, "[ perf record: Woken up %ld times to write 
data ]\n", waking);
 
+out_format:
+       if (rec->format && rec->format->cleanup)
+               rec->format->cleanup(rec->evlist, rec->format->priv);
 out_child:
        if (forks) {
                int exit_status;
@@ -748,7 +775,7 @@ out_child:
        /* this will be recalculated during process_buildids() */
        rec->samples = 0;
 
-       if (!err && !file->is_pipe) {
+       if (!err && !file->is_pipe && !rec->format) {
                rec->session->header.data_size += rec->bytes_written;
                file->size = lseek(perf_data_file__fd(file), 0, SEEK_CUR);
 
@@ -927,6 +954,37 @@ static int parse_clockid(const struct option *opt, const 
char *str, int unset)
        return -1;
 }
 
+static int parse_format(const struct option *opt, const char *str, int unset)
+{
+       struct record *rec = opt->value;
+
+       if (unset) {
+               rec->format = NULL;
+               return 0;
+       }
+
+       /* no arg passed */
+       if (!str) {
+               ui__warning("missing format argument\n");
+               return -1;
+       }
+
+       /* no setting it twice */
+       if (rec->format) {
+               ui__warning("format specified multiple times\n");
+               return -1;
+       }
+
+#ifdef HAVE_LIBBABELTRACE_SUPPORT
+       if (strcasecmp(str, "ctf") == 0) {
+               rec->format = &ctf_format;
+               return 0;
+       }
+#endif
+       ui__warning("unknown format %s, check man page\n", str);
+       return -1;
+}
+
 static int record__parse_mmap_pages(const struct option *opt,
                                    const char *str,
                                    int unset __maybe_unused)
@@ -1113,6 +1171,8 @@ struct option __record_options[] = {
                        "per thread proc mmap processing timeout in ms"),
        OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events,
                    "Record context switch events"),
+       OPT_CALLBACK(0, "format", &record, "format", "alternate output format",
+                    parse_format),
 #ifdef HAVE_LIBBPF_SUPPORT
        OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path",
                   "clang binary to use for compiling BPF scriptlets"),
diff --git a/tools/perf/util/data-convert-bt.c 
b/tools/perf/util/data-convert-bt.c
index 34cd1e4..eb40d74 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -7,6 +7,7 @@
  * Released under the GPL v2. (and only v2, not any later version)
  */
 
+#include <sys/utsname.h>
 #include <linux/compiler.h>
 #include <babeltrace/ctf-writer/writer.h>
 #include <babeltrace/ctf-writer/clock.h>
@@ -26,6 +27,7 @@
 #include "evlist.h"
 #include "evsel.h"
 #include "machine.h"
+#include "format-converter.h"
 
 #define pr_N(n, fmt, ...) \
        eprintf(n, debug_data_convert, fmt, ##__VA_ARGS__)
@@ -896,6 +898,20 @@ static int ctf_writer__setup_env(struct ctf_writer *cw,
 {
        struct perf_header *header = &session->header;
        struct bt_ctf_writer *writer = cw->writer;
+       struct utsname uts;
+       int ret;
+
+       ret = uname(&uts);
+       if (ret == 0) {
+               if (!header->env.hostname)
+                       header->env.hostname = strdup(uts.nodename);
+               if (!header->env.os_release)
+                       header->env.os_release = strdup(uts.release);
+               if (!header->env.version)
+                       header->env.version = strdup(perf_version_string);
+               if (!header->env.arch)
+                       header->env.arch = strdup(uts.machine);
+       }
 
 #define ADD(__n, __v)                                                  \
 do {                                                                   \
@@ -903,11 +919,11 @@ do {                                                      
                \
                return -1;                                              \
 } while (0)
 
-       ADD("host",    header->env.hostname);
+       ADD("host",    header->env.hostname ?: "unknown");
        ADD("sysname", "Linux");
-       ADD("release", header->env.os_release);
-       ADD("version", header->env.version);
-       ADD("machine", header->env.arch);
+       ADD("release", header->env.os_release ?: "unknown");
+       ADD("version", header->env.version ?: "unknown");
+       ADD("machine", header->env.arch ?: "unknown");
        ADD("domain", "kernel");
        ADD("tracer_name", "perf");
 
@@ -1183,3 +1199,165 @@ free_writer:
        pr_err("Error during conversion setup.\n");
        return err;
 }
+
+struct ctf_fc {
+       struct convert c;
+
+       void *partial_buf;
+       size_t partial_buf_size;
+
+       void *partial_buf_pos;
+       size_t partial_buf_rem;
+};
+
+static int ctf_fc_init(struct perf_evlist *evlist, const char *path,
+                      void **priv)
+{
+       struct perf_session dummy_session;
+       struct ctf_writer *cw;
+       struct ctf_fc *fc;
+       int err = -1;
+
+       fc = calloc(1, sizeof(*fc));
+       if (!fc)
+               goto nomem;
+
+       cw = &fc->c.writer;
+
+       memset(&dummy_session, 0, sizeof(dummy_session));
+
+        if (ctf_writer__init(cw, path))
+                goto free_mem;
+
+       if (ctf_writer__setup_env(cw, &dummy_session))
+               goto free_writer;
+
+       dummy_session.evlist = evlist;
+       if (setup_events(cw, &dummy_session))
+               goto free_writer;
+
+       if (setup_streams(cw, &dummy_session))
+               goto free_writer;
+
+       *priv = fc;
+
+       return 0;
+
+free_writer:
+       ctf_writer__cleanup(cw);
+free_mem:
+       free(fc);
+nomem:
+       pr_err("Error during conversion setup.\n");
+       return err;
+}
+
+static union perf_event *get_next_event(struct ctf_fc *fc, void *buf,
+                                       size_t size, size_t *inc)
+{
+       union perf_event *event = buf;
+
+       /* deal with existing partial event first */
+       if (fc->partial_buf_pos) {
+               if (size >= fc->partial_buf_rem)
+                       size = fc->partial_buf_rem;
+
+               memcpy(fc->partial_buf_pos, buf, size);
+               fc->partial_buf_pos += size;
+               fc->partial_buf_rem -= size;
+               *inc = size;
+
+               /* event still partial */
+               if (fc->partial_buf_rem)
+                       return NULL;
+
+               /* we have a full event */
+               fc->partial_buf_pos = NULL;
+               return fc->partial_buf;
+       }
+
+       /* deal with new paritial event */
+       if (size < event->header.size) {
+               /* realloc larger partial buffer if necessary */
+               if (fc->partial_buf_size < event->header.size) {
+                       if (fc->partial_buf) {
+                               free(fc->partial_buf);
+                               fc->partial_buf_size = 0;
+                       }
+                       fc->partial_buf = malloc(event->header.size);
+                       if (!fc->partial_buf) {
+                               *inc = 0;
+                               return NULL;
+                       }
+                       fc->partial_buf_size = event->header.size;
+               }
+
+               /* copy over the part of the event we have */
+               memcpy(fc->partial_buf, buf, size);
+               fc->partial_buf_pos = fc->partial_buf + size;
+               fc->partial_buf_rem = event->header.size - size;
+               *inc = size;
+
+               /* we now have a partial event */
+               return NULL;
+       }
+
+       /* full event available */
+       *inc = event->header.size;
+       return event;
+}
+
+static int ctf_fc_write(struct perf_evlist *evlist, void *buf, size_t size,
+                       void *priv)
+{
+       struct ctf_fc *fc = priv;
+       struct convert *c = &fc->c;
+       struct perf_sample sample;
+       struct perf_evsel *evsel;
+       union perf_event *event;
+       size_t inc;
+
+       while (size) {
+               event = get_next_event(fc, buf, size, &inc);
+
+               if (!event || event->header.type != PERF_RECORD_SAMPLE)
+                       goto skip_event;
+
+               if (perf_evlist__parse_sample(evlist, event, &sample)) {
+                       pr_err("Failed to parse event.\n");
+                       goto skip_event;
+               }
+
+               evsel = perf_evlist__id2evsel(evlist, sample.id);
+               if (!evsel) {
+                       pr_err("Failed to identify event.\n");
+                       goto skip_event;
+               }
+
+               if (process_sample_event(&c->tool, event, &sample, evsel, NULL))
+                       pr_err("Failed to process event.\n");
+skip_event:
+               size -= inc;
+               buf += inc;
+       }
+
+       return 0;
+}
+
+static int ctf_fc_cleanup(struct perf_evlist *evlist __maybe_unused, void 
*priv)
+{
+       struct ctf_fc *fc = priv;
+       struct ctf_writer *cw = &fc->c.writer;
+
+       if (ctf_writer__flush_streams(cw))
+               pr_err("Failed to flush events.\n");
+       ctf_writer__cleanup(cw);
+       free(fc);
+       return 0;
+}
+
+struct format_converter ctf_format = {
+       .init = ctf_fc_init,
+       .write = ctf_fc_write,
+       .cleanup = ctf_fc_cleanup,
+};
diff --git a/tools/perf/util/format-converter.h 
b/tools/perf/util/format-converter.h
new file mode 100644
index 0000000..bb204b1
--- /dev/null
+++ b/tools/perf/util/format-converter.h
@@ -0,0 +1,17 @@
+#ifndef __PERF_FORMAT_CONVERTER_H
+#define __PERF_FORMAT_CONVERTER_H
+
+#include "evlist.h"
+
+struct format_converter {
+       int (*init)(struct perf_evlist *evlist, const char *path, void **priv);
+       int (*write)(struct perf_evlist *evlist, void *buf, size_t size, void 
*priv);
+       int (*cleanup)(struct perf_evlist *evlist, void *priv);
+       void *priv;
+};
+
+#ifdef HAVE_LIBBABELTRACE_SUPPORT
+extern struct format_converter ctf_format;
+#endif
+
+#endif /* __PERF_FORMAT_CONVERTER_H */
-- 
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to