[RFC PATCH 2/4] perf/trace-event: Write trace.dat metadata sections during parsing

From: Tanushree Shah

Date: Mon Jun 08 2026 - 09:02:06 EST


Perf already captures the tracing metadata as a part of
data section in perf.data

When trace_dat_fp is set, write trace.dat compatible metadata
sections using the perf provided raw buffers.

Sections written:
- Initial format header (magic, version, endian, long_size,
page_size, compression, options_offset placeholder)
- Section 16: HEADER INFO (header_page + header_event)
- Section 17: FTRACE EVENT FORMATS
- Section 18: EVENT FORMATS (per system/event format files)
- Section 19: KALLSYMS
- Section 21: CMDLINES
- Section 15: STRINGS (written last after all sections)

Signed-off-by: Tanushree Shah <tshah@xxxxxxxxxxxxx>
---
tools/perf/util/trace-event-read.c | 259 ++++++++++++++++++++++++++++-
1 file changed, 252 insertions(+), 7 deletions(-)

diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index ecbbb93f0185..815577703c2e 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -19,6 +19,7 @@
#include "trace-event.h"
#include "debug.h"
#include "util.h"
+#include "trace-dat.h"

static int input_fd;

@@ -145,10 +146,9 @@ static char *read_string(void)
static int read_proc_kallsyms(struct tep_handle *pevent)
{
unsigned int size;
+ char *buf;

size = read4(pevent);
- if (!size)
- return 0;
/*
* Just skip it, now that we configure libtraceevent to use the
* tools/perf/ symbol resolver.
@@ -160,11 +160,56 @@ static int read_proc_kallsyms(struct tep_handle *pevent)
* payload", so that older tools can continue reading it and interpret
* it as "no kallsyms payload is present".
*/
- lseek(input_fd, size, SEEK_CUR);
+ /* Write kallsyms section with empty payload if no data */
+ if (!size) {
+ if (trace_dat_fp) {
+ unsigned short section_id = TRACE_DAT_SECTION_KALLSYMS;
+ unsigned short flags = 0;
+ unsigned long long section_size = sizeof(unsigned int);
+ unsigned int kallsyms_data = 0;
+ unsigned int string_id = STRID_KALLSYMS;
+
+ trace_dat_kallsyms_offset = ftell(trace_dat_fp);
+ if (!fwrite(&section_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&flags, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&string_id, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&section_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(&kallsyms_data, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+ }
+ return 0;
+ }
+ buf = malloc(size);
+ if (buf == NULL)
+ return -1;
+ if (read(input_fd, buf, size) < 0) {
+ free(buf);
+ return -1;
+ }
trace_data_size += size;
+ /* Write kallsyms section with data */
+ if (trace_dat_fp) {
+ unsigned short section_id = TRACE_DAT_SECTION_KALLSYMS;
+ unsigned int string_id = STRID_KALLSYMS;
+ unsigned long long section_size = sizeof(unsigned int) + size;
+ unsigned short flags = 0;
+
+ trace_dat_kallsyms_offset = ftell(trace_dat_fp);
+ if (!fwrite(&section_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&flags, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&string_id, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&section_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(&size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(buf, 1, size, trace_dat_fp)) {
+ free(buf);
+ return -EIO;
+ }
+ }
+ free(buf);
return 0;
}

+
static int read_ftrace_printk(struct tep_handle *pevent)
{
unsigned int size;
@@ -195,6 +240,13 @@ static int read_ftrace_printk(struct tep_handle *pevent)
static int read_header_files(struct tep_handle *pevent)
{
unsigned long long size;
+ unsigned long long header_page_size;
+ unsigned long long header_event_size;
+ char *header_event;
+ unsigned short section_id;
+ unsigned short flags;
+ unsigned int string_id;
+ unsigned long long section_size;
char *header_page;
char buf[BUFSIZ];
int ret = 0;
@@ -209,6 +261,7 @@ static int read_header_files(struct tep_handle *pevent)

size = read8(pevent);

+ header_page_size = size;
header_page = malloc(size);
if (header_page == NULL)
return -1;
@@ -227,19 +280,59 @@ static int read_header_files(struct tep_handle *pevent)
*/
tep_set_long_size(pevent, tep_get_header_page_size(pevent));
}
- free(header_page);

- if (do_read(buf, 13) < 0)
+ if (do_read(buf, 13) < 0) {
+ free(header_page);
return -1;
+ }

if (memcmp(buf, "header_event", 13) != 0) {
pr_debug("did not read header event");
+ free(header_page);
return -1;
}

size = read8(pevent);
- skip(size);
+ if (trace_dat_fp) {
+ header_event_size = size;
+ header_event = malloc(size);
+ if (header_event == NULL) {
+ free(header_page);
+ return -1;
+ }
+ if (do_read(header_event, size) < 0) {
+ free(header_page);
+ free(header_event);
+ return -1;
+ }
+ /* Write header_page and header_event to trace.dat */
+ section_id = TRACE_DAT_SECTION_HEADER;
+ flags = 0;
+ string_id = STRID_HEADERS;
+ section_size = 12 + 8 + header_page_size + 13 + 8 +
+ header_event_size;
+
+ trace_dat_header_info_offset = ftell(trace_dat_fp);
+ if (!fwrite(&section_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&flags, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&string_id, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&section_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite("header_page\0", 1, 12, trace_dat_fp) ||
+ !fwrite(&header_page_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(header_page, 1, header_page_size, trace_dat_fp) ||
+ !fwrite("header_event\0", 1, 13, trace_dat_fp) ||
+ !fwrite(&header_event_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(header_event, 1, header_event_size, trace_dat_fp)) {
+ free(header_page);
+ free(header_event);
+ return -EIO;
+ }
+ free(header_event);
+ } else {
+ skip(size);
+ }

+ free(header_page);
return ret;
}

@@ -259,6 +352,13 @@ static int read_ftrace_file(struct tep_handle *pevent, unsigned long long size)
pr_debug("error reading ftrace file.\n");
goto out;
}
+ if (trace_dat_fp) {
+ if (!fwrite(&size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(buf, 1, size, trace_dat_fp)) {
+ free(buf);
+ return -EIO;
+ }
+ }

ret = parse_ftrace_file(pevent, buf, size);
if (ret < 0)
@@ -283,6 +383,13 @@ static int read_event_file(struct tep_handle *pevent, char *sys,
ret = do_read(buf, size);
if (ret < 0)
goto out;
+ if (trace_dat_fp) {
+ if (!fwrite(&size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(buf, 1, size, trace_dat_fp)) {
+ free(buf);
+ return -EIO;
+ }
+ }

ret = parse_event_file(pevent, buf, size, sys);
if (ret < 0)
@@ -298,8 +405,31 @@ static int read_ftrace_files(struct tep_handle *pevent)
int count;
int i;
int ret;
+ long section_size_pos = 0;
+ long count_pos = 0;
+ unsigned long long section_size = 0;
+ long end_pos;

count = read4(pevent);
+ /* Write ftrace formats section to trace.dat output file */
+ if (trace_dat_fp) {
+ unsigned short section_id = TRACE_DAT_SECTION_FTRACE;
+ unsigned short flags = 0;
+ unsigned int string_id = STRID_FTRACE_FORMATS;
+
+ trace_dat_ftrace_format_offset = ftell(trace_dat_fp);
+
+ if (!fwrite(&section_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&flags, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&string_id, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+ section_size_pos = ftell(trace_dat_fp);
+ if (!fwrite(&section_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+ count_pos = ftell(trace_dat_fp);
+ if (!fwrite(&count, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+ }

for (i = 0; i < count; i++) {
size = read8(pevent);
@@ -307,6 +437,16 @@ static int read_ftrace_files(struct tep_handle *pevent)
if (ret)
return ret;
}
+ /* Fill in section size after writing all ftrace files */
+ if (trace_dat_fp) {
+ end_pos = ftell(trace_dat_fp);
+ section_size = end_pos - count_pos;
+ fseek(trace_dat_fp, section_size_pos, SEEK_SET);
+ if (!fwrite(&section_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+ fseek(trace_dat_fp, end_pos, SEEK_SET);
+ }
+
return 0;
}

@@ -318,8 +458,30 @@ static int read_event_files(struct tep_handle *pevent)
int count;
int i,x;
int ret;
+ long section_size_pos = 0;
+ long sys_count_pos = 0;
+ unsigned long long section_size = 0;
+ long end_pos;

systems = read4(pevent);
+ /* Write event formats section to trace.dat output file */
+ if (trace_dat_fp) {
+ unsigned short section_id = TRACE_DAT_SECTION_EVENTS;
+ unsigned short flags = 0;
+ unsigned int string_id = STRID_EVENT_FORMATS;
+
+ trace_dat_events_format_offset = ftell(trace_dat_fp);
+ if (!fwrite(&section_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&flags, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&string_id, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+ section_size_pos = ftell(trace_dat_fp);
+ if (!fwrite(&section_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+ sys_count_pos = ftell(trace_dat_fp);
+ if (!fwrite(&systems, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+ }

for (i = 0; i < systems; i++) {
sys = read_string();
@@ -327,6 +489,11 @@ static int read_event_files(struct tep_handle *pevent)
return -1;

count = read4(pevent);
+ if (trace_dat_fp) {
+ if (!fwrite(sys, 1, strlen(sys) + 1, trace_dat_fp) ||
+ !fwrite(&count, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+ }

for (x=0; x < count; x++) {
size = read8(pevent);
@@ -338,6 +505,16 @@ static int read_event_files(struct tep_handle *pevent)
}
free(sys);
}
+ /* Fill in section size after writing all event files */
+ if (trace_dat_fp) {
+ end_pos = ftell(trace_dat_fp);
+ section_size = end_pos - sys_count_pos;
+ fseek(trace_dat_fp, section_size_pos, SEEK_SET);
+ if (!fwrite(&section_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+ fseek(trace_dat_fp, end_pos, SEEK_SET);
+ }
+
return 0;
}

@@ -349,8 +526,25 @@ static int read_saved_cmdline(struct tep_handle *pevent)

/* it can have 0 size */
size = read8(pevent);
- if (!size)
+ /* Write cmdlines section with empty payload if no data */
+ if (!size) {
+ if (trace_dat_fp) {
+ unsigned short section_id = TRACE_DAT_SECTION_CMDLINE;
+ unsigned short flags = 0;
+ unsigned int string_id = STRID_CMDLINES;
+ unsigned long long section_size = sizeof(unsigned long long);
+ unsigned long long section_data = 0;
+
+ trace_dat_cmdline_offset = ftell(trace_dat_fp);
+ if (!fwrite(&section_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&flags, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&string_id, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&section_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(&section_data, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+ }
return 0;
+ }

buf = malloc(size + 1);
if (buf == NULL) {
@@ -363,6 +557,23 @@ static int read_saved_cmdline(struct tep_handle *pevent)
pr_debug("error reading saved cmdlines\n");
goto out;
}
+ /* Write cmdlines section with data */
+ if (trace_dat_fp) {
+ unsigned short section_id = TRACE_DAT_SECTION_CMDLINE;
+ unsigned short flags = 0;
+ unsigned int string_id = STRID_CMDLINES;
+ unsigned long long section_size = sizeof(unsigned long long) + size;
+
+ trace_dat_cmdline_offset = ftell(trace_dat_fp);
+ if (!fwrite(&section_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&flags, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&string_id, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&section_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(&size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(buf, 1, size, trace_dat_fp))
+ return -EIO;
+ }
+
buf[ret] = '\0';

parse_saved_cmdline(pevent, buf, size);
@@ -387,6 +598,7 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
int file_page_size;
struct tep_handle *pevent = NULL;
int err;
+ char magic_buf[10];

repipe = __repipe;
input_fd = fd;
@@ -398,12 +610,17 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
return -1;
}

+ if (trace_dat_fp)
+ memcpy(magic_buf, buf, 3);
+
if (do_read(buf, 7) < 0)
return -1;
if (memcmp(buf, "tracing", 7) != 0) {
pr_debug("not a trace file (missing 'tracing' tag)");
return -1;
}
+ if (trace_dat_fp)
+ memcpy(magic_buf + 3, buf, 7);

version = read_string();
if (version == NULL)
@@ -440,6 +657,28 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
tep_set_long_size(pevent, file_long_size);
tep_set_page_size(pevent, file_page_size);

+ /* Write initial file header to trace.dat */
+ if (trace_dat_fp) {
+ unsigned char endian = file_bigendian;
+ unsigned char long_size = file_long_size;
+ unsigned int page_size = file_page_size;
+ unsigned long long placeholder = 0;
+ char trace_dat_version = TRACE_DAT_VERSION;
+
+ if (!fwrite(magic_buf, 1, 10, trace_dat_fp) || /* magic + "tracing" */
+ !fwrite(&trace_dat_version, 1, 2, trace_dat_fp) ||
+ !fwrite(&endian, 1, 1, trace_dat_fp) ||
+ !fwrite(&long_size, 1, 1, trace_dat_fp) ||
+ !fwrite(&page_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite("none", 1, 4, trace_dat_fp) ||
+ !fwrite("\0", 1, 1, trace_dat_fp) ||
+ !fwrite("\0", 1, 1, trace_dat_fp))
+ return -EIO;
+ trace_dat_options_offset = ftell(trace_dat_fp);
+ if (!fwrite(&placeholder, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+ }
+
err = read_header_files(pevent);
if (err)
goto out;
@@ -460,6 +699,12 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe)
if (err)
goto out;
}
+ /* Write strings section to trace.dat output file */
+ if (trace_dat_fp) {
+ err = trace_dat__write_strings_section();
+ if (err)
+ goto out;
+ }

size = trace_data_size;
repipe = false;
--
2.53.0