From: Tanushree Shah <tshah@linux.ibm.com>
To: acme@kernel.org, jolsa@kernel.org, adrian.hunter@intel.com,
vmolnaro@redhat.com, mpetlan@redhat.com, tmricht@linux.ibm.com,
maddy@linux.ibm.com, irogers@google.com, namhyung@kernel.org
Cc: linux-perf-users@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
atrajeev@linux.ibm.com, hbathini@linux.ibm.com,
Tejas.Manhas1@ibm.com, Tanushree.Shah@ibm.com,
rostedt@goodmis.org, Tanushree Shah <tshah@linux.ibm.com>
Subject: [RFC PATCH v4 4/5] perf data: Add --to-trace-dat option for converting perf.data tracepoint events into trace.dat format
Date: Sat, 22 Aug 2026 11:57:23 +0530 [thread overview]
Message-ID: <20260822062729.882005-5-tshah@linux.ibm.com> (raw)
In-Reply-To: <20260822062729.882005-1-tshah@linux.ibm.com>
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 <tshah@linux.ibm.com>
---
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
next prev parent reply other threads:[~2026-08-22 6:28 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-22 6:27 [RFC PATCH v4 0/5] Add perf.data tracepoint events to trace.dat conversion Tanushree Shah
2026-08-22 6:27 ` [RFC PATCH v4 1/5] perf/trace-dat: Add trace.dat export infrastructure Tanushree Shah
2026-08-22 6:40 ` sashiko-bot
2026-08-22 6:27 ` [RFC PATCH v4 2/5] perf/trace-event: Write trace.dat metadata sections during parsing Tanushree Shah
2026-08-22 6:44 ` sashiko-bot
2026-08-22 6:27 ` [RFC PATCH v4 3/5] perf data-convert: Add perf.data to trace.dat conversion backend Tanushree Shah
2026-08-22 6:38 ` sashiko-bot
2026-08-22 6:27 ` Tanushree Shah [this message]
2026-08-22 6:44 ` [RFC PATCH v4 4/5] perf data: Add --to-trace-dat option for converting perf.data tracepoint events into trace.dat format sashiko-bot
2026-08-22 6:27 ` [RFC PATCH v4 5/5] perf test: Add test validating trace.dat generated by 'perf data convert --to-trace-dat' Tanushree Shah
2026-08-22 6:36 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260822062729.882005-5-tshah@linux.ibm.com \
--to=tshah@linux.ibm.com \
--cc=Tanushree.Shah@ibm.com \
--cc=Tejas.Manhas1@ibm.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=atrajeev@linux.ibm.com \
--cc=hbathini@linux.ibm.com \
--cc=irogers@google.com \
--cc=jolsa@kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=maddy@linux.ibm.com \
--cc=mpetlan@redhat.com \
--cc=namhyung@kernel.org \
--cc=rostedt@goodmis.org \
--cc=tmricht@linux.ibm.com \
--cc=vmolnaro@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox