* [RFC PATCH v2 1/5] perf/trace-dat: Add trace.dat export infrastructure
2026-08-02 18:07 [RFC PATCH v2 0/5] Add perf.data tracepoint events to trace.dat conversion Tanushree Shah
@ 2026-08-02 18:07 ` Tanushree Shah
2026-08-02 18:07 ` [RFC PATCH v2 2/5] perf/trace-event: Write trace.dat metadata sections during parsing Tanushree Shah
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Tanushree Shah @ 2026-08-02 18:07 UTC (permalink / raw)
To: acme, jolsa, adrian.hunter, vmolnaro, mpetlan, tmricht, maddy,
irogers, namhyung
Cc: linux-perf-users, linuxppc-dev, atrajeev, hbathini, Tejas.Manhas1,
Tanushree.Shah, rostedt, Tanushree Shah
Add new utility files util/trace-dat.c and util/trace-dat.h
implementing the infrastructure for exporting perf.data tracepoints
to trace.dat format compatible with trace-cmd and KernelShark.
trace-dat.c defines all globals and functions needed for:
- Per-cpu raw event buffer management (init_cpu_buffers,
collect_cpu_event, free_cpu_buffers).
- ftrace ring buffer page construction (write_page, write_cpu_dat),
including TIME_EXTEND records for large timestamp deltas.
- trace.dat section writers (write_strings_section,
write_options_section1, write_options_section2,
write_flyrecord_section).
trace-dat.h declares all globals and function prototypes to be
used by data-convert-trace.c and trace-event-read.c.
Signed-off-by: Tanushree Shah <tshah@linux.ibm.com>
---
tools/perf/util/Build | 1 +
tools/perf/util/trace-dat.c | 825 ++++++++++++++++++++++++++++++++++++
tools/perf/util/trace-dat.h | 83 ++++
3 files changed, 909 insertions(+)
create mode 100644 tools/perf/util/trace-dat.c
create mode 100644 tools/perf/util/trace-dat.h
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 70cc91d00804..c000d8032d25 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -98,6 +98,7 @@ perf-util-y += trace-event-scripting.o
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event.o
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-parse.o
perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-read.o
+perf-util-$(CONFIG_LIBTRACEEVENT) += trace-dat.o
perf-util-y += sort.o
perf-util-y += hist.o
perf-util-y += util.o
diff --git a/tools/perf/util/trace-dat.c b/tools/perf/util/trace-dat.c
new file mode 100644
index 000000000000..55a7bd982c6f
--- /dev/null
+++ b/tools/perf/util/trace-dat.c
@@ -0,0 +1,825 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2026, IBM Corporation
+ * Author: Tanushree Shah <tshah@linux.ibm.com>
+ *
+ * trace-dat.c
+ *
+ * This file implements the trace.dat format writer for perf tool.
+ * It collects trace events from multiple CPUs and writes them in
+ * the trace-cmd compatible format.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "api/fs/tracing_path.h"
+#include "trace-dat.h"
+#include "trace-event.h"
+#include "session.h"
+#include "header.h"
+#include "../perf.h"
+#include "debug.h"
+
+/* ftrace ring buffer constants for trace.dat flyrecord section
+ *
+ * Each page has a 16-byte header (timestamp + commit size), followed by
+ * variable-length records. Each record has a 4-byte header word encoding:
+ * Bits 0-4: Type/Length field (5 bits, masked by TYPE_LEN_MASK)
+ * Bits 5-31: Time delta from page base timestamp (27 bits, masked by TIME_MASK)
+ */
+#define TRACE_DAT_RECORD_HEADER_SIZE 16 /* Page header: 8-byte ts + 8-byte commit */
+#define TRACE_DAT_RECORD_TYPE_LEN_MASK 0x1F /* Extract lower 5 bits for type/length */
+#define TRACE_DAT_RECORD_TIME_SHIFT 5 /* Shift to extract time delta */
+#define TRACE_DAT_RECORD_TIME_MASK 0x07FFFFFF /* Mask for 27-bit time delta */
+#define TRACE_DAT_WORD_SIZE 4 /* Records aligned to 4-byte boundaries */
+#define TRACE_DAT_WORD_ALIGN_MASK 3
+
+/* Initial capacity for per-CPU event buffer (grows by doubling) */
+#define INITIAL_EVENT_CAPACITY 1024
+/* Initial capacity for page record array (grows by doubling) */
+#define INITIAL_PAGE_RECORD_CAPACITY 64
+/* Buffer size for reading trace_clock string from debugfs/tracefs */
+#define CLOCK_BUFFER_SIZE 256
+
+FILE *trace_dat_fp;
+int trace_dat_page_size;
+int trace_dat_nr_cpus;
+long trace_dat_options_offset;
+long trace_dat_header_info_offset;
+long trace_dat_events_format_offset;
+long trace_dat_ftrace_format_offset;
+long trace_dat_kallsyms_offset;
+long trace_dat_cmdline_offset;
+long trace_dat_next_options_offset;
+
+
+/**
+ * struct cpu_event - Single trace event from a CPU
+ * @ts: Timestamp of the event
+ * @raw: Raw event data
+ * @raw_size: Size of raw event data in bytes
+ */
+struct cpu_event {
+ unsigned long long ts;
+ void *raw;
+ unsigned int raw_size;
+};
+
+/**
+ * struct cpu_events - Collection of trace events for a single CPU
+ * @events: Array of events
+ * @count: Number of events currently stored
+ * @capacity: Maximum number of events that can be stored
+ */
+struct cpu_events {
+ struct cpu_event *events;
+ int count;
+ int capacity;
+};
+
+static struct cpu_events *trace_cpu_data;
+static long *buffer_opt_cpu_offsets_pos;
+static long opt_payload_start;
+
+/* Allocate per-cpu event buffers for tracepoint data collection */
+int trace_dat__init_cpu_buffers(int nr_cpus)
+{
+ trace_cpu_data = calloc(nr_cpus, sizeof(struct cpu_events));
+ if (!trace_cpu_data)
+ return -ENOMEM;
+ buffer_opt_cpu_offsets_pos = calloc(nr_cpus, sizeof(long));
+ if (!buffer_opt_cpu_offsets_pos) {
+ free(trace_cpu_data);
+ trace_cpu_data = NULL;
+ return -ENOMEM;
+ }
+ trace_dat_nr_cpus = nr_cpus;
+ return 0;
+}
+
+/* Store raw tracepoint event data in per-cpu buffer for trace.dat
+ * flyrecord
+ */
+int trace_dat__collect_cpu_event(int cpu, unsigned long long ts,
+ void *raw, unsigned int raw_size)
+{
+ struct cpu_events *cpu_events;
+ void *raw_copy;
+
+ if (!trace_cpu_data || cpu < 0 || cpu >= trace_dat_nr_cpus)
+ return -EINVAL;
+
+ if (!raw || raw_size == 0)
+ return -EINVAL;
+
+ cpu_events = &trace_cpu_data[cpu];
+
+ if (cpu_events->count >= cpu_events->capacity) {
+ int new_capacity = cpu_events->capacity ?
+ cpu_events->capacity * 2 : INITIAL_EVENT_CAPACITY;
+ struct cpu_event *tmp;
+
+ tmp = realloc(cpu_events->events, new_capacity * sizeof(*cpu_events->events));
+ if (!tmp)
+ return -ENOMEM;
+ cpu_events->events = tmp;
+ cpu_events->capacity = new_capacity;
+ }
+
+ raw_copy = malloc(raw_size);
+ if (!raw_copy)
+ return -ENOMEM;
+
+ memcpy(raw_copy, raw, raw_size);
+ cpu_events->events[cpu_events->count].ts = ts;
+ cpu_events->events[cpu_events->count].raw = raw_copy;
+
+ cpu_events->events[cpu_events->count].raw_size = raw_size;
+ cpu_events->count++;
+
+ return 0;
+}
+
+/* Write a single page of trace records */
+static int trace_dat__write_page(FILE *fp, unsigned long long base_ts,
+ char **records, int *rec_sizes, int nr_recs)
+{
+ unsigned long long commit = 0;
+ int offset = TRACE_DAT_RECORD_HEADER_SIZE;
+ int i;
+ char *page;
+
+ page = calloc(1, trace_dat_page_size);
+ if (!page)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_recs; i++) {
+ memcpy(page + offset, records[i], rec_sizes[i]);
+ offset += rec_sizes[i];
+ commit += rec_sizes[i];
+ }
+
+ memcpy(page, &base_ts, sizeof(base_ts));
+ memcpy(page + sizeof(base_ts), &commit, sizeof(commit));
+
+ if (!fwrite(page, 1, trace_dat_page_size, fp)) {
+ free(page);
+ return -EIO;
+ }
+ free(page);
+
+ return 0;
+}
+
+/* Write all trace data for a single CPU as trace.dat flyrecord pages */
+static int trace_dat__write_cpu_dat(FILE *fp, int cpu, unsigned long long *file_offset_out)
+{
+ struct cpu_events *cpu_events = &trace_cpu_data[cpu];
+ unsigned long long base_ts;
+ unsigned long long file_offset;
+ char **page_records = NULL;
+ int *page_rec_sizes = NULL;
+ int page_cap = 0;
+ int nr_page_recs = 0;
+ int page_size_used = 0;
+ int ret = 0;
+ int i, j;
+
+ file_offset = ftell(fp);
+ *file_offset_out = file_offset;
+
+ if (cpu_events->count == 0) {
+ char *empty_page = calloc(1, trace_dat_page_size);
+
+ if (!empty_page)
+ return -ENOMEM;
+ if (!fwrite(empty_page, 1, trace_dat_page_size, fp)) {
+ free(empty_page);
+ return -EIO;
+ }
+ free(empty_page);
+ return 0;
+ }
+
+ base_ts = cpu_events->events[0].ts;
+
+ for (i = 0; i < cpu_events->count; i++) {
+ struct cpu_event *event = &cpu_events->events[i];
+ unsigned long long time_delta = event->ts - base_ts;
+ unsigned int data_len = event->raw_size;
+ unsigned int words;
+ unsigned int type_len;
+ unsigned int payload_offset;
+ unsigned int hdr_word;
+ char *extend = NULL;
+ int extend_size = 0;
+ char *data_rec;
+ int data_rec_size;
+ int needed_size;
+
+ /* Emit TIME_EXTEND when delta does not fit in 27 bits */
+ if (time_delta > TRACE_DAT_RECORD_TIME_MASK) {
+ unsigned int extend_hdr;
+ unsigned int delta_upper;
+
+ extend_size = TRACE_DAT_RECORD_TIME_EXTEND_SIZE;
+ extend = calloc(1, extend_size);
+ if (!extend)
+ return -ENOMEM;
+
+ 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;
+
+ memcpy(extend, &extend_hdr, TRACE_DAT_WORD_SIZE);
+ memcpy(extend + TRACE_DAT_WORD_SIZE, &delta_upper,
+ TRACE_DAT_WORD_SIZE);
+
+ time_delta = 0;
+ }
+ words = (data_len + TRACE_DAT_WORD_ALIGN_MASK) / TRACE_DAT_WORD_SIZE;
+ /*
+ * Encode event length per ftrace ring buffer format:
+ * - Small events (≤28 words): type_len = word count
+ * - Large events (≥29 words): type_len = 0, followed by byte length
+ */
+ if (words <= TRACE_DAT_RECORD_TYPE_LEN_MAX) {
+ type_len = words;
+ payload_offset = TRACE_DAT_WORD_SIZE;
+ data_rec_size = TRACE_DAT_WORD_SIZE + data_len;
+ } else {
+ type_len = 0;
+ payload_offset = 2 * TRACE_DAT_WORD_SIZE;
+ data_rec_size = 2 * TRACE_DAT_WORD_SIZE + data_len;
+ }
+
+ if (data_rec_size % TRACE_DAT_WORD_SIZE)
+ data_rec_size += TRACE_DAT_WORD_SIZE -
+ (data_rec_size % TRACE_DAT_WORD_SIZE);
+
+ /* Reject records that cannot fit in a single page payload */
+ if (data_rec_size > trace_dat_page_size - TRACE_DAT_RECORD_HEADER_SIZE) {
+ free(extend);
+ ret = -E2BIG;
+ goto out_free;
+ }
+
+ needed_size = extend_size + data_rec_size;
+
+ /* 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, base_ts,
+ page_records, page_rec_sizes,
+ nr_page_recs);
+
+ for (j = 0; j < nr_page_recs; j++)
+ free(page_records[j]);
+
+ nr_page_recs = 0;
+ page_size_used = 0;
+ base_ts = event->ts;
+
+ if (ret < 0) {
+ free(extend);
+ goto out_free;
+ }
+
+ /*
+ * New page base matches this event timestamp, so no
+ * TIME_EXTEND is needed anymore.
+ */
+ free(extend);
+ extend = NULL;
+ extend_size = 0;
+ time_delta = 0;
+ }
+
+ hdr_word = (time_delta << TRACE_DAT_RECORD_TIME_SHIFT) | type_len;
+
+ data_rec = calloc(1, data_rec_size);
+ if (!data_rec) {
+ free(extend);
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ memcpy(data_rec, &hdr_word, TRACE_DAT_WORD_SIZE);
+
+ /* Large events: write actual byte length after header */
+ if (type_len == 0)
+ memcpy(data_rec + TRACE_DAT_WORD_SIZE, &data_len, TRACE_DAT_WORD_SIZE);
+
+ memcpy(data_rec + payload_offset, event->raw, data_len);
+
+ if (nr_page_recs + (extend ? 1 : 0) >= page_cap) {
+ char **new_records;
+ int *new_sizes;
+ int needed = nr_page_recs + (extend ? 1 : 0) + 1;
+ int new_capacity = page_cap ? page_cap * 2 :
+ INITIAL_PAGE_RECORD_CAPACITY;
+
+ while (new_capacity < needed)
+ new_capacity *= 2;
+
+ new_records = malloc(new_capacity * sizeof(*new_records));
+ new_sizes = malloc(new_capacity * sizeof(*new_sizes));
+ if (!new_records || !new_sizes) {
+ free(new_records);
+ free(new_sizes);
+ free(extend);
+ free(data_rec);
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ if (nr_page_recs > 0) {
+ memcpy(new_records, page_records,
+ nr_page_recs * sizeof(*new_records));
+ memcpy(new_sizes, page_rec_sizes,
+ nr_page_recs * sizeof(*new_sizes));
+ }
+
+ free(page_records);
+ free(page_rec_sizes);
+
+ page_records = new_records;
+ page_rec_sizes = new_sizes;
+ page_cap = new_capacity;
+ }
+
+ if (extend) {
+ page_records[nr_page_recs] = extend;
+ page_rec_sizes[nr_page_recs] = extend_size;
+ nr_page_recs++;
+ page_size_used += extend_size;
+ }
+
+ page_records[nr_page_recs] = data_rec;
+ page_rec_sizes[nr_page_recs] = data_rec_size;
+ nr_page_recs++;
+ page_size_used += data_rec_size;
+ }
+
+ if (nr_page_recs > 0) {
+ ret = trace_dat__write_page(fp, base_ts,
+ page_records, page_rec_sizes, nr_page_recs);
+ }
+out_free:
+ for (j = 0; j < nr_page_recs; j++)
+ free(page_records[j]);
+ free(page_records);
+ free(page_rec_sizes);
+ return ret;
+}
+
+/* Write the strings section containing section name lookup table */
+int trace_dat__write_strings_section(void)
+{
+ unsigned short section_id = TRACE_DAT_SECTION_STRINGS;
+ unsigned short flags = 0;
+ unsigned long long section_size = 0;
+ static const char * const section_names[] = {
+ "headers", /* offset 0 - strid for section 16 */
+ "ftrace event formats", /* offset 8 - strid for section 17 */
+ "events format", /* offset 29 - strid for section 18 */
+ "kallsyms", /* offset 43 - strid for section 19 */
+ "cmdlines", /* offset 52 - strid for section 21 */
+ "strings", /* offset 61 - strid for section 15 */
+ "options", /* offset 69 - strid for options 1 */
+ "options", /* offset 77 - strid for options 2 */
+ "buffer-flyrecord", /* offset 85 - strid for flyrecord 3 */
+ NULL
+ };
+
+ /* string_id points to "strings" string itself */
+ unsigned int string_id = STRID_STRINGS;
+ int i;
+
+ if (!trace_dat_fp)
+ return -EBADF;
+
+ for (i = 0; section_names[i] != NULL; i++)
+ section_size += strlen(section_names[i]) + 1;
+
+ /* write section header */
+ if (!fwrite(§ion_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(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+
+ /* write strings */
+ for (i = 0; section_names[i] != NULL; i++)
+ if (!fwrite(section_names[i], 1, strlen(section_names[i]) + 1, trace_dat_fp))
+ return -EIO;
+ return 0;
+}
+
+/* Writes options section containing CPUCOUNT, TRACECLOCK, EVENT_FORMAT, HEADER_INFO,
+ * FTRACE_EVENTS, KALLSYMS, CMDLINES options, ending with DONE option pointing to next section.
+ */
+int trace_dat__write_options_section1(void)
+{
+ unsigned short section_id = TRACE_DAT_SECTION_OPTIONS;
+ unsigned short flags = 0;
+ unsigned int string_id = STRID_OPTIONS_1;
+ unsigned long long section_size = 0;
+ long section_size_pos;
+ long payload_start;
+ unsigned long long section_start;
+ unsigned short opt_id;
+ unsigned int opt_size;
+ char clock_buf[CLOCK_BUFFER_SIZE];
+ FILE *clock_file;
+ size_t bytes_read;
+ char *path;
+ unsigned long long next_offset;
+ long end_pos;
+
+ if (!trace_dat_fp)
+ return -EBADF;
+
+ /* fill options_offset in initial format */
+ section_start = ftell(trace_dat_fp);
+
+ if (fseek(trace_dat_fp, trace_dat_options_offset, SEEK_SET) < 0 ||
+ !fwrite(§ion_start, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ fseek(trace_dat_fp, 0, SEEK_END) < 0)
+ return -EIO;
+
+ /* write section header */
+ if (!fwrite(§ion_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(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+
+ payload_start = ftell(trace_dat_fp);
+
+ /* CPUCOUNT option */
+ opt_id = TRACE_DAT_OPTION_CPUCOUNT;
+ opt_size = sizeof(unsigned int);
+
+ if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&trace_dat_nr_cpus, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+
+ /* TRACECLOCK option */
+ opt_id = TRACE_DAT_OPTION_TRACECLOCK;
+
+ path = get_tracing_file("trace_clock");
+ if (path) {
+ clock_file = fopen(path, "r");
+ put_tracing_file(path);
+ } else {
+ clock_file = NULL;
+ }
+ if (clock_file) {
+ bytes_read = fread(clock_buf, 1, sizeof(clock_buf) - 1, clock_file);
+ fclose(clock_file);
+ clock_buf[bytes_read] = '\0';
+ } else {
+ strcpy(clock_buf, "local\n");
+ bytes_read = strlen(clock_buf);
+ }
+ opt_size = bytes_read + 1;
+ if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(clock_buf, 1, opt_size, trace_dat_fp))
+ return -EIO;
+
+ /* EVENT option */
+ opt_id = TRACE_DAT_OPTION_EVENT;
+ opt_size = sizeof(unsigned long long);
+
+ if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&trace_dat_events_format_offset, sizeof(unsigned long long),
+ 1, trace_dat_fp))
+ return -EIO;
+
+ /* HEADER option */
+ opt_id = TRACE_DAT_OPTION_HEADER;
+ opt_size = sizeof(unsigned long long);
+
+ if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&trace_dat_header_info_offset, sizeof(unsigned long long),
+ 1, trace_dat_fp))
+ return -EIO;
+
+ /* FTRACE option */
+ opt_id = TRACE_DAT_OPTION_FTRACE;
+ opt_size = sizeof(unsigned long long);
+
+ if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&trace_dat_ftrace_format_offset, sizeof(unsigned long long),
+ 1, trace_dat_fp))
+ return -EIO;
+
+ /* KALLSYMS option */
+ opt_id = TRACE_DAT_OPTION_KALLSYMS;
+ opt_size = sizeof(unsigned long long);
+
+ if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&trace_dat_kallsyms_offset, sizeof(unsigned long long),
+ 1, trace_dat_fp))
+ return -EIO;
+
+ /* CMDLINE option */
+ opt_id = TRACE_DAT_OPTION_CMDLINE;
+ opt_size = sizeof(unsigned long long);
+
+ if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&trace_dat_cmdline_offset, sizeof(unsigned long long),
+ 1, trace_dat_fp))
+ return -EIO;
+
+ /* DONE option id - next_options_offset filled later */
+ opt_id = TRACE_DAT_OPTION_DONE;
+ opt_size = sizeof(unsigned long long);
+ next_offset = 0; /* placeholder */
+
+ trace_dat_next_options_offset = ftell(trace_dat_fp);
+ if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&next_offset, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+
+ /* fill section size */
+ end_pos = ftell(trace_dat_fp);
+
+ section_size = end_pos - payload_start;
+ if (fseek(trace_dat_fp, section_size_pos, SEEK_SET) < 0 ||
+ !fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ fseek(trace_dat_fp, end_pos, SEEK_SET) < 0)
+ return -EIO;
+
+ return 0;
+
+}
+
+/* Writes options section containing BUFFER option with flyrecord section
+ * (flyrecord section offset, clock type, page size, CPU count,
+ * per-CPU offsets/sizes) and DONE option.
+ */
+int trace_dat__write_options_section2(void)
+{
+ unsigned short section_id = TRACE_DAT_SECTION_OPTIONS;
+ unsigned short flags = 0;
+ unsigned int string_id = STRID_OPTIONS_2;
+ unsigned long long section_size = 0;
+ long section_size_pos;
+ long payload_start;
+ int cpu;
+ unsigned short opt_id = TRACE_DAT_OPTION_BUFFER;
+ unsigned int opt_size = 0;
+ long opt_size_pos;
+ unsigned long long data_offset = 0;
+ unsigned int page_size = (unsigned int)trace_dat_page_size;
+ const char *clock = "local";
+ unsigned long long next;
+ long end_pos;
+ unsigned long long cpu_offset;
+ unsigned long long cpu_size;
+ unsigned short done_id;
+ unsigned int done_size;
+
+ if (!trace_dat_fp)
+ return -EINVAL;
+
+ /* fill done1 next offset - points to this section */
+ next = ftell(trace_dat_fp);
+
+ if (fseek(trace_dat_fp, trace_dat_next_options_offset + 2 + 4, SEEK_SET) < 0 ||
+ !fwrite(&next, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ fseek(trace_dat_fp, 0, SEEK_END) < 0)
+ return -EIO;
+
+ /* write section header */
+ if (!fwrite(§ion_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(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+
+ payload_start = ftell(trace_dat_fp);
+
+ /* BUFFER option */
+ if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp))
+ return -EIO;
+ opt_size_pos = ftell(trace_dat_fp);
+ if (!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+ opt_payload_start = ftell(trace_dat_fp);
+
+ /* data_offset placeholder */
+ if (!fwrite(&data_offset, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite("\0", 1, 1, trace_dat_fp) ||
+ !fwrite(clock, 1, strlen(clock) + 1, trace_dat_fp) ||
+ !fwrite(&page_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&trace_dat_nr_cpus, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+
+ /* per cpu: cpu_id + offset placeholder + size */
+ for (cpu = 0; cpu < trace_dat_nr_cpus; cpu++) {
+ cpu_offset = 0; /* filled in write_flyrecord */
+ cpu_size = 0; /* filled in write_flyrecord */
+
+ if (!fwrite(&cpu, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+ buffer_opt_cpu_offsets_pos[cpu] = ftell(trace_dat_fp);
+ if (!fwrite(&cpu_offset, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(&cpu_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+ }
+
+ /* fill opt_size */
+ end_pos = ftell(trace_dat_fp);
+
+ opt_size = end_pos - opt_payload_start;
+ fseek(trace_dat_fp, opt_size_pos, SEEK_SET);
+ if (!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp))
+ return -EIO;
+ fseek(trace_dat_fp, end_pos, SEEK_SET);
+
+ /* DONE id=0 */
+ done_id = TRACE_DAT_OPTION_DONE;
+ done_size = sizeof(unsigned long long);
+ /* No additional options sections follow this one */
+ next = 0;
+
+ if (!fwrite(&done_id, sizeof(unsigned short), 1, trace_dat_fp) ||
+ !fwrite(&done_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(&next, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+
+ /* fill section size */
+ end_pos = ftell(trace_dat_fp);
+
+ section_size = end_pos - payload_start;
+ fseek(trace_dat_fp, section_size_pos, SEEK_SET);
+ if (!fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ return -EIO;
+ fseek(trace_dat_fp, end_pos, SEEK_SET);
+
+ return 0;
+
+}
+
+int trace_dat__write_flyrecord_section(void)
+{
+ unsigned short section_id = TRACE_DAT_SECTION_FLYRECORD;
+ unsigned short flags = 0;
+ unsigned int string_id = STRID_BUFFER_FLYRECORD;
+ unsigned long long section_size = 0;
+ long section_size_pos;
+ long flyrecord_start;
+ long after_header;
+ long padding_needed;
+ unsigned long long *cpu_offsets;
+ unsigned long long *cpu_sizes;
+ int cpu;
+ int ret = 0;
+ char *pad;
+ unsigned long long start;
+ long end_pos;
+
+ if (!trace_dat_fp)
+ return -EINVAL;
+
+ cpu_offsets = calloc(trace_dat_nr_cpus, sizeof(unsigned long long));
+ cpu_sizes = calloc(trace_dat_nr_cpus, sizeof(unsigned long long));
+ if (!cpu_offsets || !cpu_sizes) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ flyrecord_start = ftell(trace_dat_fp);
+ if (flyrecord_start < 0) {
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ /* section header */
+ if (!fwrite(§ion_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)) {
+ ret = -EIO;
+ goto cleanup;
+ }
+ section_size_pos = ftell(trace_dat_fp);
+ if (!fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ /* Align to page boundary */
+ after_header = ftell(trace_dat_fp);
+ padding_needed = (trace_dat_page_size -
+ (after_header % trace_dat_page_size)) % trace_dat_page_size;
+
+ if (padding_needed > 0) {
+ pad = calloc(1, padding_needed);
+ if (!pad) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ if (!fwrite(pad, 1, padding_needed, trace_dat_fp)) {
+ free(pad);
+ ret = -EIO;
+ goto cleanup;
+ }
+ free(pad);
+ }
+
+ /* write per-cpu trace data */
+ for (cpu = 0; cpu < trace_dat_nr_cpus; cpu++) {
+ start = ftell(trace_dat_fp);
+
+ ret = trace_dat__write_cpu_dat(trace_dat_fp, cpu, &cpu_offsets[cpu]);
+
+ if (ret < 0) {
+ pr_err("Failed to write CPU %d data\n", cpu);
+ goto cleanup;
+ }
+ cpu_sizes[cpu] = ftell(trace_dat_fp) - start;
+ }
+
+ /* fill section size */
+ end_pos = ftell(trace_dat_fp);
+
+ section_size = end_pos - after_header;
+ if (fseek(trace_dat_fp, section_size_pos, SEEK_SET) < 0 ||
+ !fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ ret = -EIO;
+ goto cleanup;
+ }
+ if (fseek(trace_dat_fp, end_pos, SEEK_SET) < 0) {
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ /* fill cpu offsets and sizes in BUFFER option */
+ for (cpu = 0; cpu < trace_dat_nr_cpus; cpu++) {
+ if (fseek(trace_dat_fp, buffer_opt_cpu_offsets_pos[cpu], SEEK_SET) < 0 ||
+ !fwrite(&cpu_offsets[cpu], sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(&cpu_sizes[cpu], sizeof(unsigned long long), 1, trace_dat_fp)) {
+ ret = -EIO;
+ goto cleanup;
+ }
+ }
+
+ /* fill data offset in buffer option */
+ if (fseek(trace_dat_fp, opt_payload_start, SEEK_SET) < 0 ||
+ !fwrite(&flyrecord_start, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ if (fseek(trace_dat_fp, 0, SEEK_END) < 0) {
+ ret = -EIO;
+ goto cleanup;
+ }
+
+
+cleanup:
+ free(cpu_offsets);
+ free(cpu_sizes);
+ return ret;
+}
+
+/* Free all per-CPU event buffers */
+void trace_dat__free_cpu_buffers(void)
+{
+ int cpu;
+
+ if (!trace_cpu_data)
+ return;
+
+ for (cpu = 0; cpu < trace_dat_nr_cpus; cpu++) {
+ int i;
+
+ for (i = 0; i < trace_cpu_data[cpu].count; i++)
+ free(trace_cpu_data[cpu].events[i].raw);
+ free(trace_cpu_data[cpu].events);
+ }
+ free(trace_cpu_data);
+ trace_cpu_data = NULL;
+ free(buffer_opt_cpu_offsets_pos);
+ buffer_opt_cpu_offsets_pos = NULL;
+ trace_dat_nr_cpus = 0;
+}
diff --git a/tools/perf/util/trace-dat.h b/tools/perf/util/trace-dat.h
new file mode 100644
index 000000000000..9aec37b708d4
--- /dev/null
+++ b/tools/perf/util/trace-dat.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2026, IBM Corporation
+ * Author: Tanushree Shah <tshah@linux.ibm.com>
+ */
+
+#ifndef __PERF_TRACE_DAT_H
+#define __PERF_TRACE_DAT_H
+
+#include <stdio.h>
+
+/* trace.dat file format version */
+#define TRACE_DAT_VERSION '7'
+
+/*
+ * Section IDs for trace.dat format
+ */
+#define TRACE_DAT_SECTION_OPTIONS 0
+#define TRACE_DAT_SECTION_FLYRECORD 3
+#define TRACE_DAT_SECTION_STRINGS 15
+#define TRACE_DAT_SECTION_HEADER 16
+#define TRACE_DAT_SECTION_FTRACE 17
+#define TRACE_DAT_SECTION_EVENTS 18
+#define TRACE_DAT_SECTION_KALLSYMS 19
+#define TRACE_DAT_SECTION_CMDLINE 21
+
+/*
+ * Option IDs for trace.dat options sections
+ */
+#define TRACE_DAT_OPTION_DONE 0
+#define TRACE_DAT_OPTION_BUFFER 3
+#define TRACE_DAT_OPTION_TRACECLOCK 4
+#define TRACE_DAT_OPTION_CPUCOUNT 8
+#define TRACE_DAT_OPTION_HEADER 16
+#define TRACE_DAT_OPTION_FTRACE 17
+#define TRACE_DAT_OPTION_EVENT 18
+#define TRACE_DAT_OPTION_KALLSYMS 19
+#define TRACE_DAT_OPTION_CMDLINE 21
+
+/*
+ * String offsets in the strings section
+ * These point to null-terminated strings used as section names
+ */
+#define STRID_HEADERS 0
+#define STRID_FTRACE_FORMATS 8
+#define STRID_EVENT_FORMATS 29
+#define STRID_KALLSYMS 43
+#define STRID_CMDLINES 52
+#define STRID_STRINGS 61
+#define STRID_OPTIONS_1 69
+#define STRID_OPTIONS_2 77
+#define STRID_BUFFER_FLYRECORD 85
+
+struct perf_session;
+
+extern FILE *trace_dat_fp;
+extern int trace_dat_page_size;
+extern int trace_dat_nr_cpus;
+extern long trace_dat_options_offset;
+extern long trace_dat_header_info_offset;
+extern long trace_dat_events_format_offset;
+extern long trace_dat_ftrace_format_offset;
+extern long trace_dat_kallsyms_offset;
+extern long trace_dat_cmdline_offset;
+extern long trace_dat_next_options_offset;
+
+/* collect and manage per-cpu tracepoint event buffers */
+#define TRACE_DAT_RECORD_TYPE_TIME_EXTEND 30
+#define TRACE_DAT_RECORD_TIME_EXTEND_SIZE 8
+#define TRACE_DAT_RECORD_TYPE_LEN_MAX 28
+
+int trace_dat__init_cpu_buffers(int nr_cpus);
+int trace_dat__collect_cpu_event(int cpu, unsigned long long ts,
+ void *raw, unsigned int raw_size);
+void trace_dat__free_cpu_buffers(void);
+
+/* write trace.dat file sections */
+int trace_dat__write_options_section1(void);
+int trace_dat__write_options_section2(void);
+int trace_dat__write_flyrecord_section(void);
+int trace_dat__write_strings_section(void);
+
+#endif /* __PERF_TRACE_DAT_H */
--
2.47.3
^ permalink raw reply related [flat|nested] 6+ messages in thread* [RFC PATCH v2 2/5] perf/trace-event: Write trace.dat metadata sections during parsing
2026-08-02 18:07 [RFC PATCH v2 0/5] Add perf.data tracepoint events to trace.dat conversion Tanushree Shah
2026-08-02 18:07 ` [RFC PATCH v2 1/5] perf/trace-dat: Add trace.dat export infrastructure Tanushree Shah
@ 2026-08-02 18:07 ` Tanushree Shah
2026-08-02 18:07 ` [RFC PATCH v2 3/5] perf data-convert: Add perf.data to trace.dat conversion backend Tanushree Shah
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Tanushree Shah @ 2026-08-02 18:07 UTC (permalink / raw)
To: acme, jolsa, adrian.hunter, vmolnaro, mpetlan, tmricht, maddy,
irogers, namhyung
Cc: linux-perf-users, linuxppc-dev, atrajeev, hbathini, Tejas.Manhas1,
Tanushree.Shah, rostedt, Tanushree Shah
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)
Introduce to_file_u16/u32/u64 helpers wrapping tep_read_number() to
write all trace.dat metadata in the recorded machine's byte
order, ensuring correct cross-platform compatibility.
Signed-off-by: Tanushree Shah <tshah@linux.ibm.com>
---
tools/perf/util/trace-dat.c | 212 ++++++++++++--------
tools/perf/util/trace-dat.h | 40 +++-
tools/perf/util/trace-event-read.c | 307 ++++++++++++++++++++++++++++-
3 files changed, 465 insertions(+), 94 deletions(-)
diff --git a/tools/perf/util/trace-dat.c b/tools/perf/util/trace-dat.c
index 55a7bd982c6f..f71e03716e27 100644
--- a/tools/perf/util/trace-dat.c
+++ b/tools/perf/util/trace-dat.c
@@ -43,6 +43,7 @@
/* Buffer size for reading trace_clock string from debugfs/tracefs */
#define CLOCK_BUFFER_SIZE 256
+bool trace_dat_write_failed;
FILE *trace_dat_fp;
int trace_dat_page_size;
int trace_dat_nr_cpus;
@@ -143,10 +144,12 @@ int trace_dat__collect_cpu_event(int cpu, unsigned long long ts,
}
/* Write a single page of trace records */
-static int trace_dat__write_page(FILE *fp, unsigned long long base_ts,
+static int trace_dat__write_page(FILE *fp, struct tep_handle *pevent, unsigned long long base_ts,
char **records, int *rec_sizes, int nr_recs)
{
unsigned long long commit = 0;
+ unsigned long long ts_out;
+ unsigned long long commit_out;
int offset = TRACE_DAT_RECORD_HEADER_SIZE;
int i;
char *page;
@@ -161,8 +164,12 @@ static int trace_dat__write_page(FILE *fp, unsigned long long base_ts,
commit += rec_sizes[i];
}
- memcpy(page, &base_ts, sizeof(base_ts));
- memcpy(page + sizeof(base_ts), &commit, sizeof(commit));
+ /* Byte-swap page header for cross-arch compatibility */
+ ts_out = to_file_u64(pevent, base_ts);
+ commit_out = to_file_u64(pevent, commit);
+
+ memcpy(page, &ts_out, sizeof(ts_out));
+ memcpy(page + sizeof(ts_out), &commit_out, sizeof(commit_out));
if (!fwrite(page, 1, trace_dat_page_size, fp)) {
free(page);
@@ -174,7 +181,8 @@ static int trace_dat__write_page(FILE *fp, unsigned long long base_ts,
}
/* Write all trace data for a single CPU as trace.dat flyrecord pages */
-static int trace_dat__write_cpu_dat(FILE *fp, int cpu, unsigned long long *file_offset_out)
+static int trace_dat__write_cpu_dat(FILE *fp, struct tep_handle *pevent,
+ int cpu, unsigned long long *file_offset_out)
{
struct cpu_events *cpu_events = &trace_cpu_data[cpu];
unsigned long long base_ts;
@@ -229,11 +237,18 @@ static int trace_dat__write_cpu_dat(FILE *fp, int cpu, unsigned long long *file_
if (!extend)
return -ENOMEM;
- 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;
+ if (tep_is_file_bigendian(pevent)) {
+ extend_hdr = (time_delta & TRACE_DAT_RECORD_TIME_MASK) |
+ (TRACE_DAT_RECORD_TYPE_TIME_EXTEND << 27);
+ delta_upper = time_delta >> 27;
+ } else {
+ 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;
+ }
+ extend_hdr = to_file_u32(pevent, extend_hdr); /* still needed */
+ delta_upper = to_file_u32(pevent, delta_upper); /* still needed */
memcpy(extend, &extend_hdr, TRACE_DAT_WORD_SIZE);
memcpy(extend + TRACE_DAT_WORD_SIZE, &delta_upper,
@@ -273,7 +288,7 @@ static int trace_dat__write_cpu_dat(FILE *fp, int cpu, unsigned long long *file_
/* 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, base_ts,
+ ret = trace_dat__write_page(fp, pevent, base_ts,
page_records, page_rec_sizes,
nr_page_recs);
@@ -299,7 +314,13 @@ static int trace_dat__write_cpu_dat(FILE *fp, int cpu, unsigned long long *file_
time_delta = 0;
}
- hdr_word = (time_delta << TRACE_DAT_RECORD_TIME_SHIFT) | type_len;
+ if (tep_is_file_bigendian(pevent))
+ hdr_word = ((unsigned int)time_delta & TRACE_DAT_RECORD_TIME_MASK) |
+ (type_len << 27);
+ else
+ hdr_word = ((unsigned int)time_delta << TRACE_DAT_RECORD_TIME_SHIFT) |
+ type_len;
+ hdr_word = to_file_u32(pevent, hdr_word);
data_rec = calloc(1, data_rec_size);
if (!data_rec) {
@@ -311,8 +332,11 @@ static int trace_dat__write_cpu_dat(FILE *fp, int cpu, unsigned long long *file_
memcpy(data_rec, &hdr_word, TRACE_DAT_WORD_SIZE);
/* Large events: write actual byte length after header */
- if (type_len == 0)
- memcpy(data_rec + TRACE_DAT_WORD_SIZE, &data_len, TRACE_DAT_WORD_SIZE);
+ if (type_len == 0) {
+ unsigned int data_len_out = to_file_u32(pevent, data_len);
+
+ memcpy(data_rec + TRACE_DAT_WORD_SIZE, &data_len_out, TRACE_DAT_WORD_SIZE);
+ }
memcpy(data_rec + payload_offset, event->raw, data_len);
@@ -366,7 +390,7 @@ static int trace_dat__write_cpu_dat(FILE *fp, int cpu, unsigned long long *file_
}
if (nr_page_recs > 0) {
- ret = trace_dat__write_page(fp, base_ts,
+ ret = trace_dat__write_page(fp, pevent, base_ts,
page_records, page_rec_sizes, nr_page_recs);
}
out_free:
@@ -378,11 +402,11 @@ static int trace_dat__write_cpu_dat(FILE *fp, int cpu, unsigned long long *file_
}
/* Write the strings section containing section name lookup table */
-int trace_dat__write_strings_section(void)
+int trace_dat__write_strings_section(struct tep_handle *pevent)
{
- unsigned short section_id = TRACE_DAT_SECTION_STRINGS;
- unsigned short flags = 0;
unsigned long long section_size = 0;
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_STRINGS);
+ unsigned short flags = to_file_u16(pevent, 0);
static const char * const section_names[] = {
"headers", /* offset 0 - strid for section 16 */
"ftrace event formats", /* offset 8 - strid for section 17 */
@@ -397,7 +421,7 @@ int trace_dat__write_strings_section(void)
};
/* string_id points to "strings" string itself */
- unsigned int string_id = STRID_STRINGS;
+ unsigned int string_id = to_file_u32(pevent, STRID_STRINGS);
int i;
if (!trace_dat_fp)
@@ -405,47 +429,59 @@ int trace_dat__write_strings_section(void)
for (i = 0; section_names[i] != NULL; i++)
section_size += strlen(section_names[i]) + 1;
+ section_size = to_file_u64(pevent, section_size);
/* write section header */
if (!fwrite(§ion_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(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp))
- return -EIO;
+ !fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ pr_warning("Failed to write strings section\n");
+ trace_dat_write_failed = true;
+ }
/* write strings */
for (i = 0; section_names[i] != NULL; i++)
- if (!fwrite(section_names[i], 1, strlen(section_names[i]) + 1, trace_dat_fp))
- return -EIO;
+ if (!fwrite(section_names[i], 1, strlen(section_names[i]) + 1, trace_dat_fp)) {
+ pr_warning("Failed to write strings section\n");
+ trace_dat_write_failed = true;
+ }
return 0;
}
/* Writes options section containing CPUCOUNT, TRACECLOCK, EVENT_FORMAT, HEADER_INFO,
* FTRACE_EVENTS, KALLSYMS, CMDLINES options, ending with DONE option pointing to next section.
*/
-int trace_dat__write_options_section1(void)
+int trace_dat__write_options_section1(struct tep_handle *pevent)
{
- unsigned short section_id = TRACE_DAT_SECTION_OPTIONS;
- unsigned short flags = 0;
- unsigned int string_id = STRID_OPTIONS_1;
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_OPTIONS);
+ unsigned short flags = to_file_u16(pevent, 0);
+ unsigned int string_id = to_file_u32(pevent, STRID_OPTIONS_1);
unsigned long long section_size = 0;
long section_size_pos;
long payload_start;
unsigned long long section_start;
unsigned short opt_id;
unsigned int opt_size;
+ unsigned int raw_opt_size;
+ unsigned int nr_cpus;
char clock_buf[CLOCK_BUFFER_SIZE];
FILE *clock_file;
size_t bytes_read;
char *path;
unsigned long long next_offset;
+ unsigned long long events_off;
+ unsigned long long header_off;
+ unsigned long long ftrace_off;
+ unsigned long long kallsyms_off;
+ unsigned long long cmdline_off;
long end_pos;
if (!trace_dat_fp)
return -EBADF;
/* fill options_offset in initial format */
- section_start = ftell(trace_dat_fp);
+ section_start = to_file_u64(pevent, ftell(trace_dat_fp));
if (fseek(trace_dat_fp, trace_dat_options_offset, SEEK_SET) < 0 ||
!fwrite(§ion_start, sizeof(unsigned long long), 1, trace_dat_fp) ||
@@ -464,16 +500,17 @@ int trace_dat__write_options_section1(void)
payload_start = ftell(trace_dat_fp);
/* CPUCOUNT option */
- opt_id = TRACE_DAT_OPTION_CPUCOUNT;
- opt_size = sizeof(unsigned int);
+ opt_id = to_file_u16(pevent, TRACE_DAT_OPTION_CPUCOUNT);
+ opt_size = to_file_u32(pevent, sizeof(unsigned int));
+ nr_cpus = to_file_u32(pevent, trace_dat_nr_cpus);
if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
- !fwrite(&trace_dat_nr_cpus, sizeof(unsigned int), 1, trace_dat_fp))
+ !fwrite(&nr_cpus, sizeof(unsigned int), 1, trace_dat_fp))
return -EIO;
/* TRACECLOCK option */
- opt_id = TRACE_DAT_OPTION_TRACECLOCK;
+ opt_id = to_file_u16(pevent, TRACE_DAT_OPTION_TRACECLOCK);
path = get_tracing_file("trace_clock");
if (path) {
@@ -490,66 +527,72 @@ int trace_dat__write_options_section1(void)
strcpy(clock_buf, "local\n");
bytes_read = strlen(clock_buf);
}
- opt_size = bytes_read + 1;
+ raw_opt_size = bytes_read + 1;
+ opt_size = to_file_u32(pevent, bytes_read + 1);
if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
- !fwrite(clock_buf, 1, opt_size, trace_dat_fp))
+ !fwrite(clock_buf, 1, raw_opt_size, trace_dat_fp))
return -EIO;
/* EVENT option */
- opt_id = TRACE_DAT_OPTION_EVENT;
- opt_size = sizeof(unsigned long long);
+ opt_id = to_file_u16(pevent, TRACE_DAT_OPTION_EVENT);
+ opt_size = to_file_u32(pevent, sizeof(unsigned long long));
+ events_off = to_file_u64(pevent, trace_dat_events_format_offset);
if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
- !fwrite(&trace_dat_events_format_offset, sizeof(unsigned long long),
+ !fwrite(&events_off, sizeof(unsigned long long),
1, trace_dat_fp))
return -EIO;
/* HEADER option */
- opt_id = TRACE_DAT_OPTION_HEADER;
- opt_size = sizeof(unsigned long long);
+ opt_id = to_file_u16(pevent, TRACE_DAT_OPTION_HEADER);
+ opt_size = to_file_u32(pevent, sizeof(unsigned long long));
+ header_off = to_file_u64(pevent, trace_dat_header_info_offset);
if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
- !fwrite(&trace_dat_header_info_offset, sizeof(unsigned long long),
+ !fwrite(&header_off, sizeof(unsigned long long),
1, trace_dat_fp))
return -EIO;
/* FTRACE option */
- opt_id = TRACE_DAT_OPTION_FTRACE;
- opt_size = sizeof(unsigned long long);
+ opt_id = to_file_u16(pevent, TRACE_DAT_OPTION_FTRACE);
+ opt_size = to_file_u32(pevent, sizeof(unsigned long long));
+ ftrace_off = to_file_u64(pevent, trace_dat_ftrace_format_offset);
if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
- !fwrite(&trace_dat_ftrace_format_offset, sizeof(unsigned long long),
+ !fwrite(&ftrace_off, sizeof(unsigned long long),
1, trace_dat_fp))
return -EIO;
/* KALLSYMS option */
- opt_id = TRACE_DAT_OPTION_KALLSYMS;
- opt_size = sizeof(unsigned long long);
+ opt_id = to_file_u16(pevent, TRACE_DAT_OPTION_KALLSYMS);
+ opt_size = to_file_u32(pevent, sizeof(unsigned long long));
+ kallsyms_off = to_file_u64(pevent, trace_dat_kallsyms_offset);
if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
- !fwrite(&trace_dat_kallsyms_offset, sizeof(unsigned long long),
+ !fwrite(&kallsyms_off, sizeof(unsigned long long),
1, trace_dat_fp))
return -EIO;
/* CMDLINE option */
- opt_id = TRACE_DAT_OPTION_CMDLINE;
- opt_size = sizeof(unsigned long long);
+ opt_id = to_file_u16(pevent, TRACE_DAT_OPTION_CMDLINE);
+ opt_size = to_file_u32(pevent, sizeof(unsigned long long));
+ cmdline_off = to_file_u64(pevent, trace_dat_cmdline_offset);
if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
- !fwrite(&trace_dat_cmdline_offset, sizeof(unsigned long long),
+ !fwrite(&cmdline_off, sizeof(unsigned long long),
1, trace_dat_fp))
return -EIO;
/* DONE option id - next_options_offset filled later */
- opt_id = TRACE_DAT_OPTION_DONE;
- opt_size = sizeof(unsigned long long);
- next_offset = 0; /* placeholder */
+ opt_id = to_file_u16(pevent, TRACE_DAT_OPTION_DONE);
+ opt_size = to_file_u32(pevent, sizeof(unsigned long long));
+ next_offset = to_file_u64(pevent, 0);
trace_dat_next_options_offset = ftell(trace_dat_fp);
if (!fwrite(&opt_id, sizeof(unsigned short), 1, trace_dat_fp) ||
@@ -560,7 +603,7 @@ int trace_dat__write_options_section1(void)
/* fill section size */
end_pos = ftell(trace_dat_fp);
- section_size = end_pos - payload_start;
+ section_size = to_file_u64(pevent, end_pos - payload_start);
if (fseek(trace_dat_fp, section_size_pos, SEEK_SET) < 0 ||
!fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
fseek(trace_dat_fp, end_pos, SEEK_SET) < 0)
@@ -574,20 +617,21 @@ int trace_dat__write_options_section1(void)
* (flyrecord section offset, clock type, page size, CPU count,
* per-CPU offsets/sizes) and DONE option.
*/
-int trace_dat__write_options_section2(void)
+int trace_dat__write_options_section2(struct tep_handle *pevent)
{
- unsigned short section_id = TRACE_DAT_SECTION_OPTIONS;
- unsigned short flags = 0;
- unsigned int string_id = STRID_OPTIONS_2;
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_OPTIONS);
+ unsigned short flags = to_file_u16(pevent, 0);
+ unsigned int string_id = to_file_u32(pevent, STRID_OPTIONS_2);
unsigned long long section_size = 0;
long section_size_pos;
long payload_start;
int cpu;
- unsigned short opt_id = TRACE_DAT_OPTION_BUFFER;
- unsigned int opt_size = 0;
+ unsigned short opt_id = to_file_u16(pevent, TRACE_DAT_OPTION_BUFFER);
+ unsigned int opt_size = 0; /* filled after payload written */
long opt_size_pos;
unsigned long long data_offset = 0;
- unsigned int page_size = (unsigned int)trace_dat_page_size;
+ unsigned int page_size = to_file_u32(pevent, (unsigned int)trace_dat_page_size);
+ unsigned int nr_cpus = to_file_u32(pevent, trace_dat_nr_cpus);
const char *clock = "local";
unsigned long long next;
long end_pos;
@@ -600,7 +644,7 @@ int trace_dat__write_options_section2(void)
return -EINVAL;
/* fill done1 next offset - points to this section */
- next = ftell(trace_dat_fp);
+ next = to_file_u64(pevent, ftell(trace_dat_fp));
if (fseek(trace_dat_fp, trace_dat_next_options_offset + 2 + 4, SEEK_SET) < 0 ||
!fwrite(&next, sizeof(unsigned long long), 1, trace_dat_fp) ||
@@ -631,15 +675,16 @@ int trace_dat__write_options_section2(void)
!fwrite("\0", 1, 1, trace_dat_fp) ||
!fwrite(clock, 1, strlen(clock) + 1, trace_dat_fp) ||
!fwrite(&page_size, sizeof(unsigned int), 1, trace_dat_fp) ||
- !fwrite(&trace_dat_nr_cpus, sizeof(unsigned int), 1, trace_dat_fp))
+ !fwrite(&nr_cpus, sizeof(unsigned int), 1, trace_dat_fp))
return -EIO;
/* per cpu: cpu_id + offset placeholder + size */
for (cpu = 0; cpu < trace_dat_nr_cpus; cpu++) {
+ unsigned int cpu_out = to_file_u32(pevent, cpu);
cpu_offset = 0; /* filled in write_flyrecord */
- cpu_size = 0; /* filled in write_flyrecord */
+ cpu_size = 0; /* filled in write_flyrecord */
- if (!fwrite(&cpu, sizeof(unsigned int), 1, trace_dat_fp))
+ if (!fwrite(&cpu_out, sizeof(unsigned int), 1, trace_dat_fp))
return -EIO;
buffer_opt_cpu_offsets_pos[cpu] = ftell(trace_dat_fp);
if (!fwrite(&cpu_offset, sizeof(unsigned long long), 1, trace_dat_fp) ||
@@ -650,17 +695,17 @@ int trace_dat__write_options_section2(void)
/* fill opt_size */
end_pos = ftell(trace_dat_fp);
- opt_size = end_pos - opt_payload_start;
- fseek(trace_dat_fp, opt_size_pos, SEEK_SET);
- if (!fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp))
+ opt_size = to_file_u32(pevent, end_pos - opt_payload_start);
+ if (fseek(trace_dat_fp, opt_size_pos, SEEK_SET) < 0 ||
+ !fwrite(&opt_size, sizeof(unsigned int), 1, trace_dat_fp) ||
+ fseek(trace_dat_fp, end_pos, SEEK_SET) < 0)
return -EIO;
- fseek(trace_dat_fp, end_pos, SEEK_SET);
/* DONE id=0 */
- done_id = TRACE_DAT_OPTION_DONE;
- done_size = sizeof(unsigned long long);
+ done_id = to_file_u16(pevent, TRACE_DAT_OPTION_DONE);
+ done_size = to_file_u32(pevent, sizeof(unsigned long long));
/* No additional options sections follow this one */
- next = 0;
+ next = to_file_u64(pevent, 0);
if (!fwrite(&done_id, sizeof(unsigned short), 1, trace_dat_fp) ||
!fwrite(&done_size, sizeof(unsigned int), 1, trace_dat_fp) ||
@@ -670,24 +715,25 @@ int trace_dat__write_options_section2(void)
/* fill section size */
end_pos = ftell(trace_dat_fp);
- section_size = end_pos - payload_start;
- fseek(trace_dat_fp, section_size_pos, SEEK_SET);
- if (!fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp))
+ section_size = to_file_u64(pevent, end_pos - payload_start);
+ if (fseek(trace_dat_fp, section_size_pos, SEEK_SET) < 0 ||
+ !fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ fseek(trace_dat_fp, end_pos, SEEK_SET) < 0)
return -EIO;
- fseek(trace_dat_fp, end_pos, SEEK_SET);
return 0;
}
-int trace_dat__write_flyrecord_section(void)
+int trace_dat__write_flyrecord_section(struct tep_handle *pevent)
{
- unsigned short section_id = TRACE_DAT_SECTION_FLYRECORD;
- unsigned short flags = 0;
- unsigned int string_id = STRID_BUFFER_FLYRECORD;
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_FLYRECORD);
+ unsigned short flags = to_file_u16(pevent, 0);
+ unsigned int string_id = to_file_u32(pevent, STRID_BUFFER_FLYRECORD);
unsigned long long section_size = 0;
long section_size_pos;
long flyrecord_start;
+ unsigned long long flyrecord_out;
long after_header;
long padding_needed;
unsigned long long *cpu_offsets;
@@ -750,7 +796,7 @@ int trace_dat__write_flyrecord_section(void)
for (cpu = 0; cpu < trace_dat_nr_cpus; cpu++) {
start = ftell(trace_dat_fp);
- ret = trace_dat__write_cpu_dat(trace_dat_fp, cpu, &cpu_offsets[cpu]);
+ ret = trace_dat__write_cpu_dat(trace_dat_fp, pevent, cpu, &cpu_offsets[cpu]);
if (ret < 0) {
pr_err("Failed to write CPU %d data\n", cpu);
@@ -762,7 +808,7 @@ int trace_dat__write_flyrecord_section(void)
/* fill section size */
end_pos = ftell(trace_dat_fp);
- section_size = end_pos - after_header;
+ section_size = to_file_u64(pevent, end_pos - after_header);
if (fseek(trace_dat_fp, section_size_pos, SEEK_SET) < 0 ||
!fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp)) {
ret = -EIO;
@@ -775,6 +821,8 @@ int trace_dat__write_flyrecord_section(void)
/* fill cpu offsets and sizes in BUFFER option */
for (cpu = 0; cpu < trace_dat_nr_cpus; cpu++) {
+ cpu_offsets[cpu] = to_file_u64(pevent, cpu_offsets[cpu]);
+ cpu_sizes[cpu] = to_file_u64(pevent, cpu_sizes[cpu]);
if (fseek(trace_dat_fp, buffer_opt_cpu_offsets_pos[cpu], SEEK_SET) < 0 ||
!fwrite(&cpu_offsets[cpu], sizeof(unsigned long long), 1, trace_dat_fp) ||
!fwrite(&cpu_sizes[cpu], sizeof(unsigned long long), 1, trace_dat_fp)) {
@@ -784,8 +832,9 @@ int trace_dat__write_flyrecord_section(void)
}
/* fill data offset in buffer option */
+ flyrecord_out = to_file_u64(pevent, flyrecord_start);
if (fseek(trace_dat_fp, opt_payload_start, SEEK_SET) < 0 ||
- !fwrite(&flyrecord_start, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ !fwrite(&flyrecord_out, sizeof(unsigned long long), 1, trace_dat_fp)) {
ret = -EIO;
goto cleanup;
}
@@ -795,7 +844,6 @@ int trace_dat__write_flyrecord_section(void)
goto cleanup;
}
-
cleanup:
free(cpu_offsets);
free(cpu_sizes);
diff --git a/tools/perf/util/trace-dat.h b/tools/perf/util/trace-dat.h
index 9aec37b708d4..5985083b275a 100644
--- a/tools/perf/util/trace-dat.h
+++ b/tools/perf/util/trace-dat.h
@@ -8,9 +8,13 @@
#define __PERF_TRACE_DAT_H
#include <stdio.h>
+#include <stdbool.h>
+#include <event-parse.h>
+#include <byteswap.h>
+#include "util.h"
/* trace.dat file format version */
-#define TRACE_DAT_VERSION '7'
+#define TRACE_DAT_VERSION "7"
/*
* Section IDs for trace.dat format
@@ -51,6 +55,32 @@
#define STRID_OPTIONS_2 77
#define STRID_BUFFER_FLYRECORD 85
+/*
+ * Set by trace.dat section writers (kallsyms, ftrace, events etc.) on
+ * fwrite() failure; checked by trace_convert__perf2dat() to clean up.
+ */
+extern bool trace_dat_write_failed;
+
+/*
+ * tep handle from the recording machine; set by trace_convert__perf2dat()
+ * before options/flyrecord sections are written.
+ */
+
+static inline uint16_t to_file_u16(struct tep_handle *pevent, uint16_t val)
+{
+ return tep_read_number(pevent, &val, 2);
+}
+
+static inline uint32_t to_file_u32(struct tep_handle *pevent, uint32_t val)
+{
+ return tep_read_number(pevent, &val, 4);
+}
+
+static inline uint64_t to_file_u64(struct tep_handle *pevent, uint64_t val)
+{
+ return tep_read_number(pevent, &val, 8);
+}
+
struct perf_session;
extern FILE *trace_dat_fp;
@@ -75,9 +105,9 @@ int trace_dat__collect_cpu_event(int cpu, unsigned long long ts,
void trace_dat__free_cpu_buffers(void);
/* write trace.dat file sections */
-int trace_dat__write_options_section1(void);
-int trace_dat__write_options_section2(void);
-int trace_dat__write_flyrecord_section(void);
-int trace_dat__write_strings_section(void);
+int trace_dat__write_options_section1(struct tep_handle *pevent);
+int trace_dat__write_options_section2(struct tep_handle *pevent);
+int trace_dat__write_flyrecord_section(struct tep_handle *pevent);
+int trace_dat__write_strings_section(struct tep_handle *pevent);
#endif /* __PERF_TRACE_DAT_H */
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index ecbbb93f0185..7e38e9c8f507 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,59 @@ 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 && !trace_dat_write_failed) {
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_KALLSYMS);
+ unsigned short flags = to_file_u16(pevent, 0);
+ unsigned int string_id = to_file_u32(pevent, STRID_KALLSYMS);
+ unsigned long long section_size = to_file_u64(pevent, sizeof(unsigned int));
+ unsigned int kallsyms_data = to_file_u32(pevent, 0);
+
+ trace_dat_kallsyms_offset = ftell(trace_dat_fp);
+ if (!fwrite(§ion_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(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(&kallsyms_data, sizeof(unsigned int), 1, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat kallsyms section\n");
+ trace_dat_write_failed = true;
+ }
+ }
+ return 0;
+ }
+ buf = malloc(size);
+ if (buf == NULL)
+ return -1;
+ if (do_read(buf, size) < 0) {
+ free(buf);
+ return -1;
+ }
trace_data_size += size;
+ /* Write kallsyms section with data */
+ if (trace_dat_fp && !trace_dat_write_failed) {
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_KALLSYMS);
+ unsigned int string_id = to_file_u32(pevent, STRID_KALLSYMS);
+ unsigned long long section_size = to_file_u64(pevent, sizeof(unsigned int) + size);
+ unsigned short flags = to_file_u16(pevent, 0);
+ unsigned int size_out = to_file_u32(pevent, size);
+
+ trace_dat_kallsyms_offset = ftell(trace_dat_fp);
+ if (!fwrite(§ion_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(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(&size_out, sizeof(unsigned int), 1, trace_dat_fp) ||
+ !fwrite(buf, 1, size, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat kallsyms section\n");
+ trace_dat_write_failed = true;
+ }
+ }
+ free(buf);
return 0;
}
+
static int read_ftrace_printk(struct tep_handle *pevent)
{
unsigned int size;
@@ -195,6 +243,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 +264,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 +283,63 @@ 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 && !trace_dat_write_failed) {
+ unsigned long long header_page_size_out;
+ unsigned long long header_event_size_out;
+
+ 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 = to_file_u16(pevent, TRACE_DAT_SECTION_HEADER);
+ flags = to_file_u16(pevent, 0);
+ string_id = to_file_u32(pevent, STRID_HEADERS);
+ section_size = to_file_u64(pevent, 12 + 8 + header_page_size +
+ 13 + 8 + header_event_size);
+ header_page_size_out = to_file_u64(pevent, header_page_size);
+ header_event_size_out = to_file_u64(pevent, header_event_size);
+
+ trace_dat_header_info_offset = ftell(trace_dat_fp);
+ if (!fwrite(§ion_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(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite("header_page\0", 1, 12, trace_dat_fp) ||
+ !fwrite(&header_page_size_out, 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_out, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(header_event, 1, header_event_size, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat header section\n");
+ trace_dat_write_failed = true;
+ }
+ free(header_event);
+ } else {
+ skip(size);
+ }
+ free(header_page);
return ret;
}
@@ -259,6 +359,15 @@ 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 && !trace_dat_write_failed) {
+ unsigned long long size_out = to_file_u64(pevent, size);
+
+ if (!fwrite(&size_out, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(buf, 1, size, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat ftrace event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ }
ret = parse_ftrace_file(pevent, buf, size);
if (ret < 0)
@@ -283,6 +392,15 @@ 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 && !trace_dat_write_failed) {
+ unsigned long long size_out = to_file_u64(pevent, size);
+
+ if (!fwrite(&size_out, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(buf, 1, size, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ }
ret = parse_event_file(pevent, buf, size, sys);
if (ret < 0)
@@ -298,8 +416,39 @@ 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 && !trace_dat_write_failed) {
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_FTRACE);
+ unsigned short flags = to_file_u16(pevent, 0);
+ unsigned int string_id = to_file_u32(pevent, STRID_FTRACE_FORMATS);
+ unsigned int count_out = to_file_u32(pevent, count);
+
+ section_size = to_file_u64(pevent, 0);
+ trace_dat_ftrace_format_offset = ftell(trace_dat_fp);
+
+ if (!fwrite(§ion_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)) {
+ pr_warning("Failed to write trace.dat ftrace event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ section_size_pos = ftell(trace_dat_fp);
+ if (!fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat ftrace event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ count_pos = ftell(trace_dat_fp);
+ if (!fwrite(&count_out, sizeof(unsigned int), 1, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat ftrace event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ }
for (i = 0; i < count; i++) {
size = read8(pevent);
@@ -307,6 +456,18 @@ 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 && !trace_dat_write_failed) {
+ end_pos = ftell(trace_dat_fp);
+ section_size = to_file_u64(pevent, end_pos - count_pos);
+ if (fseek(trace_dat_fp, section_size_pos, SEEK_SET) < 0 ||
+ !fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ fseek(trace_dat_fp, end_pos, SEEK_SET) < 0) {
+ pr_warning("Failed to write trace.dat ftrace event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ }
+
return 0;
}
@@ -318,8 +479,38 @@ 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 && !trace_dat_write_failed) {
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_EVENTS);
+ unsigned short flags = to_file_u16(pevent, 0);
+ unsigned int string_id = to_file_u32(pevent, STRID_EVENT_FORMATS);
+ unsigned int systems_out = to_file_u32(pevent, systems);
+
+ section_size = to_file_u64(pevent, 0);
+ trace_dat_events_format_offset = ftell(trace_dat_fp);
+ if (!fwrite(§ion_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)) {
+ pr_warning("Failed to write trace.dat event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ section_size_pos = ftell(trace_dat_fp);
+ if (!fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ sys_count_pos = ftell(trace_dat_fp);
+ if (!fwrite(&systems_out, sizeof(unsigned int), 1, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ }
for (i = 0; i < systems; i++) {
sys = read_string();
@@ -327,6 +518,15 @@ static int read_event_files(struct tep_handle *pevent)
return -1;
count = read4(pevent);
+ if (trace_dat_fp && !trace_dat_write_failed) {
+ unsigned int count_out = to_file_u32(pevent, count);
+
+ if (!fwrite(sys, 1, strlen(sys) + 1, trace_dat_fp) ||
+ !fwrite(&count_out, sizeof(unsigned int), 1, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ }
for (x=0; x < count; x++) {
size = read8(pevent);
@@ -338,6 +538,18 @@ static int read_event_files(struct tep_handle *pevent)
}
free(sys);
}
+ /* Fill in section size after writing all event files */
+ if (trace_dat_fp && !trace_dat_write_failed) {
+ end_pos = ftell(trace_dat_fp);
+ section_size = to_file_u64(pevent, end_pos - sys_count_pos);
+ fseek(trace_dat_fp, section_size_pos, SEEK_SET);
+ if (!fwrite(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat event formats section\n");
+ trace_dat_write_failed = true;
+ }
+ fseek(trace_dat_fp, end_pos, SEEK_SET);
+ }
+
return 0;
}
@@ -349,8 +561,28 @@ 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 && !trace_dat_write_failed) {
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_CMDLINE);
+ unsigned short flags = to_file_u16(pevent, 0);
+ unsigned int string_id = to_file_u32(pevent, STRID_CMDLINES);
+ unsigned long long section_size =
+ to_file_u64(pevent, sizeof(unsigned long long));
+ unsigned long long section_data = to_file_u64(pevent, 0);
+
+ trace_dat_cmdline_offset = ftell(trace_dat_fp);
+ if (!fwrite(§ion_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(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(§ion_data, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat cmdlines section\n");
+ trace_dat_write_failed = true;
+ }
+ }
return 0;
+ }
buf = malloc(size + 1);
if (buf == NULL) {
@@ -363,6 +595,27 @@ 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 && !trace_dat_write_failed) {
+ unsigned short section_id = to_file_u16(pevent, TRACE_DAT_SECTION_CMDLINE);
+ unsigned short flags = to_file_u16(pevent, 0);
+ unsigned int string_id = to_file_u32(pevent, STRID_CMDLINES);
+ unsigned long long section_size =
+ to_file_u64(pevent, sizeof(unsigned long long) + size);
+ unsigned long long size_out = to_file_u64(pevent, size);
+
+ trace_dat_cmdline_offset = ftell(trace_dat_fp);
+ if (!fwrite(§ion_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(§ion_size, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(&size_out, sizeof(unsigned long long), 1, trace_dat_fp) ||
+ !fwrite(buf, 1, size, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat cmdlines section\n");
+ trace_dat_write_failed = true;
+ }
+ }
+
buf[ret] = '\0';
parse_saved_cmdline(pevent, buf, size);
@@ -387,6 +640,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 +652,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 +699,34 @@ 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 && !trace_dat_write_failed) {
+ unsigned char endian = file_bigendian;
+ unsigned char long_size = file_long_size;
+ unsigned int page_size = to_file_u32(pevent, file_page_size);
+ unsigned long long placeholder = to_file_u64(pevent, 0);
+
+ 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)) {
+ pr_warning("Failed to write trace.dat initalfile header\n");
+ trace_dat_write_failed = true;
+ }
+
+ if (!trace_dat_write_failed) {
+ trace_dat_options_offset = ftell(trace_dat_fp);
+ if (!fwrite(&placeholder, sizeof(unsigned long long), 1, trace_dat_fp)) {
+ pr_warning("Failed to write trace.dat initial file header\n");
+ trace_dat_write_failed = true;
+ }
+ }
+ }
+
err = read_header_files(pevent);
if (err)
goto out;
@@ -460,6 +747,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 && !trace_dat_write_failed) {
+ err = trace_dat__write_strings_section(pevent);
+ if (err)
+ goto out;
+ }
size = trace_data_size;
repipe = false;
--
2.47.3
^ permalink raw reply related [flat|nested] 6+ messages in thread