Linux Perf Users
 help / color / mirror / Atom feed
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


  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