Add new command-line option to perf data convert for generating
trace.dat output files.

The --to-trace-dat option:
- Accepts output filename for trace.dat format
- Mutually exclusive with --to-ctf and --to-json
- Calls trace_convert__perf2dat() to perform conversion

Usage:
  $ perf record -e sched:* -a sleep 1
  $ perf data convert --to-trace-dat=trace.dat
  $ trace-cmd report trace.dat

Document the new option in Documentation/perf-data.txt alongside
the existing --to-ctf and --to-json entries.

Honor the --time filtering option in trace_convert__perf2dat() the
same way the JSON and CTF converters do, using
perf_time__parse_for_ranges() and perf_time__ranges_skip_sample()
to skip samples outside the requested time range.

Signed-off-by: Tanushree Shah <[email protected]>
---
 tools/perf/Documentation/perf-data.txt |  7 +++++
 tools/perf/builtin-data.c              | 43 ++++++++++++++++++++++++++
 tools/perf/util/data-convert-trace.c   | 21 +++++++++++++
 tools/perf/util/trace-dat.c            | 20 +++++++++---
 tools/perf/util/trace-dat.h            |  1 +
 5 files changed, 87 insertions(+), 5 deletions(-)

diff --git a/tools/perf/Documentation/perf-data.txt 
b/tools/perf/Documentation/perf-data.txt
index 20f178d61ed7..578ba6357183 100644
--- a/tools/perf/Documentation/perf-data.txt
+++ b/tools/perf/Documentation/perf-data.txt
@@ -30,6 +30,13 @@ OPTIONS for 'convert'
 --to-json::
        Triggers JSON conversion. Specify the JSON filename to output.
 
+--to-trace-dat::
+       Triggers trace.dat conversion. Converts perf.data tracepoint events
+       to trace.dat format v7, compatible with trace-cmd and KernelShark.
+       Only PERF_TYPE_TRACEPOINT events are converted. Specify the
+       trace.dat filename to output. Requires libtraceevent support.
+       Mutually exclusive with --to-ctf and --to-json.
+
 --tod::
        Convert time to wall clock time.
 
diff --git a/tools/perf/builtin-data.c b/tools/perf/builtin-data.c
index 1dd73ed6bdcb..c9c863197d02 100644
--- a/tools/perf/builtin-data.c
+++ b/tools/perf/builtin-data.c
@@ -30,6 +30,11 @@ static const char *data_usage[] = {
 
 static const char *to_json;
 static const char *to_ctf;
+
+#ifdef HAVE_LIBTRACEEVENT
+static const char *trace_dat_output;
+#endif
+
 static struct perf_data_convert_opts opts = {
        .force = false,
        .all = false,
@@ -46,6 +51,10 @@ static const struct option data_options[] = {
                OPT_BOOLEAN(0, "all", &opts.all, "Convert all events"),
                OPT_STRING(0, "time", &opts.time_str, "str",
                           "Time span of interest (start,stop)"),
+#ifdef HAVE_LIBTRACEEVENT
+               OPT_STRING(0, "to-trace-dat", &trace_dat_output,
+                          "file", "Convert to trace.dat format using perf.data 
tracepoints"),
+#endif
                OPT_END()
        };
 
@@ -63,10 +72,44 @@ static int cmd_data_convert(int argc, const char **argv)
                pr_err("You cannot specify both --to-ctf and --to-json.\n");
                return -1;
        }
+#ifdef HAVE_LIBTRACEEVENT
+       if (trace_dat_output && (to_json || to_ctf)) {
+               pr_err("You cannot specify --to-trace-dat with --to-ctf or 
--to-json.\n");
+               return -1;
+       }
+#endif
+
+#ifdef HAVE_LIBBABELTRACE_SUPPORT
+       #ifdef HAVE_LIBTRACEEVENT
+       if (!to_json && !to_ctf && !trace_dat_output) {
+               pr_err("You must specify one of --to-ctf, --to-json, or 
--to-trace-dat.\n");
+               return -1;
+       }
+       #else
        if (!to_json && !to_ctf) {
                pr_err("You must specify one of --to-ctf or --to-json.\n");
                return -1;
        }
+       #endif
+#else
+       #ifdef HAVE_LIBTRACEEVENT
+       if (!to_json && !trace_dat_output) {
+               pr_err("You must specify --to-json or --to-trace-dat.\n");
+               return -1;
+       }
+       #else
+       if (!to_json) {
+               pr_err("You must specify --to-json.\n");
+               return -1;
+       }
+       #endif
+#endif
+
+#ifdef HAVE_LIBTRACEEVENT
+       if (trace_dat_output)
+               return trace_convert__perf2dat(input_name ? input_name : 
"perf.data",
+                                              trace_dat_output, &opts);
+#endif
 
        if (to_json)
                return bt_convert__perf2json(input_name, to_json, &opts);
diff --git a/tools/perf/util/data-convert-trace.c 
b/tools/perf/util/data-convert-trace.c
index 445479fae888..425b3aaa3f02 100644
--- a/tools/perf/util/data-convert-trace.c
+++ b/tools/perf/util/data-convert-trace.c
@@ -22,6 +22,7 @@
 #include "evsel.h"
 #include "tool.h"
 #include "debug.h"
+#include "time-utils.h"
 #include "trace-dat.h"
 #include "trace-event.h"
 #include "event.h"
@@ -31,6 +32,10 @@
 struct trace_convert {
        struct perf_tool tool;
        u64 events_count;
+       struct perf_time_interval *ptime_range;
+       int range_size;
+       int range_num;
+       u64 skipped;
 };
 
 /* Session handle and init flag used for lazy CPU buffer init in pipe mode */
@@ -86,6 +91,11 @@ static int process_sample_event(const struct perf_tool *tool,
                cpu_buffers_initialized = true;
        }
 
+       if (perf_time__ranges_skip_sample(tc->ptime_range, tc->range_num, 
sample->time)) {
+               tc->skipped++;
+               return 0;
+       }
+
        if (trace_dat__collect_cpu_event(sample->cpu, sample->time,
                                sample->raw_data, sample->raw_size) < 0) {
                pr_err("Failed to collect CPU event\n");
@@ -185,6 +195,15 @@ int trace_convert__perf2dat(const char *input, const char 
*to_trace,
        /* Stash session for lazy CPU buffer init on first sample (pipe and 
normal mode) */
        trace_dat_session = session;
 
+       if (opts->time_str) {
+               ret = perf_time__parse_for_ranges(opts->time_str, session,
+                               &tc.ptime_range,
+                               &tc.range_size,
+                               &tc.range_num);
+               if (ret < 0)
+                       goto out_delete;
+       }
+
        /* Process all events - collects raw data per-cpu */
        ret = perf_session__process_events(session);
        if (ret < 0) {
@@ -230,6 +249,8 @@ int trace_convert__perf2dat(const char *input, const char 
*to_trace,
 out_delete:
        if (cpu_buffers_initialized)
                trace_dat__free_cpu_buffers();
+       if (tc.ptime_range)
+               zfree(&tc.ptime_range);
        perf_session__delete(session);
        trace_dat_session = NULL;
 out_close:
diff --git a/tools/perf/util/trace-dat.c b/tools/perf/util/trace-dat.c
index f71e03716e27..1b236745bcac 100644
--- a/tools/perf/util/trace-dat.c
+++ b/tools/perf/util/trace-dat.c
@@ -194,6 +194,7 @@ static int trace_dat__write_cpu_dat(FILE *fp, struct 
tep_handle *pevent,
        int page_size_used = 0;
        int ret = 0;
        int i, j;
+       unsigned long long page_base_ts;
 
        file_offset = ftell(fp);
        *file_offset_out = file_offset;
@@ -212,6 +213,7 @@ static int trace_dat__write_cpu_dat(FILE *fp, struct 
tep_handle *pevent,
        }
 
        base_ts = cpu_events->events[0].ts;
+       page_base_ts = base_ts;
 
        for (i = 0; i < cpu_events->count; i++) {
                struct cpu_event *event = &cpu_events->events[i];
@@ -234,8 +236,10 @@ static int trace_dat__write_cpu_dat(FILE *fp, struct 
tep_handle *pevent,
 
                        extend_size = TRACE_DAT_RECORD_TIME_EXTEND_SIZE;
                        extend = calloc(1, extend_size);
-                       if (!extend)
-                               return -ENOMEM;
+                       if (!extend) {
+                               ret = -ENOMEM;
+                               goto out_free;
+                       }
 
                        if (tep_is_file_bigendian(pevent)) {
                                extend_hdr = (time_delta & 
TRACE_DAT_RECORD_TIME_MASK) |
@@ -245,7 +249,7 @@ static int trace_dat__write_cpu_dat(FILE *fp, struct 
tep_handle *pevent,
                                extend_hdr = ((time_delta & 
TRACE_DAT_RECORD_TIME_MASK) <<
                                        TRACE_DAT_RECORD_TIME_SHIFT) |
                                        TRACE_DAT_RECORD_TYPE_TIME_EXTEND;
-                               delta_upper = time_delta >> 
TRACE_DAT_RECORD_TIME_SHIFT;
+                               delta_upper = time_delta >> 27;
                        }
                        extend_hdr  = to_file_u32(pevent, extend_hdr);   /* 
still needed */
                        delta_upper = to_file_u32(pevent, delta_upper);   /* 
still needed */
@@ -288,7 +292,7 @@ static int trace_dat__write_cpu_dat(FILE *fp, struct 
tep_handle *pevent,
                /* Check page fit BEFORE allocating data record */
                if (page_size_used + needed_size >
                        trace_dat_page_size - TRACE_DAT_RECORD_HEADER_SIZE) {
-                       ret = trace_dat__write_page(fp, pevent, base_ts,
+                       ret = trace_dat__write_page(fp, pevent, page_base_ts,
                                        page_records, page_rec_sizes,
                                        nr_page_recs);
 
@@ -298,6 +302,7 @@ static int trace_dat__write_cpu_dat(FILE *fp, struct 
tep_handle *pevent,
                        nr_page_recs = 0;
                        page_size_used = 0;
                        base_ts = event->ts;
+                       page_base_ts = event->ts;
 
                        if (ret < 0) {
                                free(extend);
@@ -312,6 +317,7 @@ static int trace_dat__write_cpu_dat(FILE *fp, struct 
tep_handle *pevent,
                        extend = NULL;
                        extend_size = 0;
                        time_delta = 0;
+                       base_ts = event->ts;
                }
 
                if (tep_is_file_bigendian(pevent))
@@ -387,10 +393,11 @@ static int trace_dat__write_cpu_dat(FILE *fp, struct 
tep_handle *pevent,
                page_rec_sizes[nr_page_recs] = data_rec_size;
                nr_page_recs++;
                page_size_used += data_rec_size;
+               base_ts = event->ts;
        }
 
        if (nr_page_recs > 0) {
-               ret = trace_dat__write_page(fp, pevent, base_ts,
+               ret = trace_dat__write_page(fp, pevent, page_base_ts,
                                page_records, page_rec_sizes, nr_page_recs);
        }
 out_free:
@@ -861,6 +868,9 @@ void trace_dat__free_cpu_buffers(void)
        for (cpu = 0; cpu < trace_dat_nr_cpus; cpu++) {
                int i;
 
+               if (!trace_cpu_data[cpu].events)
+                       continue;
+
                for (i = 0; i < trace_cpu_data[cpu].count; i++)
                        free(trace_cpu_data[cpu].events[i].raw);
                free(trace_cpu_data[cpu].events);
diff --git a/tools/perf/util/trace-dat.h b/tools/perf/util/trace-dat.h
index 5985083b275a..40041dac60d4 100644
--- a/tools/perf/util/trace-dat.h
+++ b/tools/perf/util/trace-dat.h
@@ -11,6 +11,7 @@
 #include <stdbool.h>
 #include <event-parse.h>
 #include <byteswap.h>
+#include <stdint.h>
 #include "util.h"
 
 /* trace.dat file format version */
-- 
2.47.1


Reply via email to