[lttng-dev] [RFC PATCH 2/2] babeltrace legacy LTT 2.6 -> CTF 1.8 converter
Jan Glauber
jan.glauber at gmail.com
Fri Apr 19 06:20:30 EDT 2013
Add a standalone converter that can read LTT 2.6 trace format
and convert it to CTF 1.8.
- complete conversion of LTT metadata to a custom CTF
which is readable by babeltrace / Eclipse
- exploit CTF streams to avoid conversion of old format event IDs
- converting a 32 bit trace on a 64 bit host is possible
- conversion of event file headers but direct copy of event data
so conversion is reasonable fast
- maintains CPU information by creating CTF headers for event data
- conversion of UST trace data
Signed-off-by: Jan Glauber <jan.glauber at gmail.com>
---
converter/babeltrace-legacy.c | 1184 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 1184 insertions(+)
create mode 100644 converter/babeltrace-legacy.c
diff --git a/converter/babeltrace-legacy.c b/converter/babeltrace-legacy.c
new file mode 100644
index 0000000..e542a37
--- /dev/null
+++ b/converter/babeltrace-legacy.c
@@ -0,0 +1,1184 @@
+/*
+ * babeltrace-legacy.c
+ *
+ * BabelTrace - Convert legacy LTT 2.6 to CTF
+ *
+ * Copyright 2013 Harman Becker Automotive Systems GmbH
+ * Author: Jan Glauber <jan.glauber at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Requires the parse_format function from LTT.
+ */
+
+#define _GNU_SOURCE
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <inttypes.h>
+#include <search.h>
+
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/ctf/types.h>
+#include <babeltrace/uuid.h>
+#include "babeltrace/ltt-type-parser.h"
+
+#define LTT_MAGIC_NUMBER_LE 0x00D6B7ED
+#define LTT_MAGIC_NUMBER_BE 0xEDB7D600
+
+#define TSC_SHIFT 27
+#define TSC_MASK ((1UL << TSC_SHIFT) - 1)
+#define CTF_PACKET_SIZE PAGE_SIZE
+#define CTF_PACKET_MASK (CTF_PACKET_SIZE - 1)
+#define CTF_METADATA_MAGIC 0x75d11d57;
+#define CTF_TRACEDATA_MAGIC 0xc1fc1fc1;
+#define MAX_NR_STREAMS 1000
+#define MAX_CHANNEL_NAME 50
+
+/*
+ * The default is to assume the trace data is from a 32 bit system. This works
+ * regardless if the host is 32 bit or 64 bit. You need to remove the define
+ * only if _both_ host and target are 64 bit. Note that the conversion of
+ * 64 bit target to 32 bit host is not supported.
+ */
+#define CONVERT_FROM_32BIT
+
+char current_channel[MAX_CHANNEL_NAME];
+
+int babeltrace_debug, babeltrace_verbose;
+static char *s_inputname;
+static char *s_outputname;
+static int s_help;
+static unsigned char s_uuid[BABELTRACE_UUID_LEN];
+
+void *map; /* mapped file object, only one is accessed at once */
+size_t map_len; /* needed for munmap */
+int nr_events;
+int nr_streams;
+size_t written;
+int ltt_size_int, ltt_size_long, ltt_size_ptr, ltt_size_size_t;
+
+char zero_page[4096];
+
+/*
+ * If compiling under -m64 the struct must be packed, otherwise gcc pads up to
+ * the next 8 byte boundary and the struct ends on a 4 byte boundary...
+ */
+struct ltt_subbuffer_header {
+ uint64_t cycle_count_begin; /* Cycle count at subbuffer start */
+ uint64_t cycle_count_end; /* Cycle count at subbuffer end */
+ uint32_t magic_number; /*
+ * Trace magic number.
+ * contains endianness information.
+ */
+ uint8_t major_version;
+ uint8_t minor_version;
+ uint8_t arch_size; /* Architecture pointer size */
+ uint8_t alignment; /* LTT data alignment */
+ uint64_t start_time_sec; /* NTP-corrected start time */
+ uint64_t start_time_usec;
+ uint64_t start_freq; /*
+ * Frequency at trace start,
+ * used all along the trace.
+ */
+ uint32_t freq_scale; /* Frequency scaling (divisor) */
+ uint32_t data_size; /* Size of data in subbuffer */
+ uint32_t sb_size; /* Subbuffer size (including padding) */
+ uint32_t events_lost; /*
+ * Events lost in this subbuffer since
+ * the beginning of the trace.
+ * (may overflow)
+ */
+ uint32_t subbuf_corrupt; /*
+ * Corrupted (lost) subbuffers since
+ * the begginig of the trace.
+ * (may overflow)
+ */
+ uint8_t header_end[0]; /* End of header */
+} __attribute__ ((packed));
+
+struct ltt_subbuffer_header ltt_hdr;
+
+/* the large ltt event header */
+struct ltt_event_header {
+ uint32_t packed; /* 5 bit id, 27 bit tsc */
+ uint16_t event_id;
+ uint16_t event_size;
+ uint32_t packet_size;
+ uint64_t timestamp;
+} __attribute__ ((aligned(4), packed));
+
+struct ltt_event_info {
+ char *channel_name;
+ char *event_name;
+ uint16_t event_id;
+ uint8_t size_int;
+ uint8_t size_long;
+ uint8_t size_ptr;
+ uint8_t size_size_t;
+ uint8_t align;
+ char *format;
+} __attribute__ ((packed));
+
+struct lttng_metadata_header {
+ uint32_t magic;
+ uint8_t uuid[16];
+ uint32_t checksum;
+ uint32_t content_size;
+ uint32_t packet_size;
+ uint8_t compr_scheme;
+ uint8_t enc_scheme;
+ uint8_t checksum_scheme;
+ uint8_t major;
+ uint8_t minor;
+} __attribute__ ((packed));
+
+struct lttng_packet_header {
+ uint32_t magic;
+ uint8_t uuid[16];
+ uint32_t stream_id;
+} __attribute__ ((packed));
+
+struct lttng_packet_context {
+ uint64_t timestamp_begin;
+ uint64_t timestamp_end;
+ uint64_t content_size;
+ uint64_t packet_size;
+#ifdef CONVERT_FROM_32BIT /* 32 bit trace is converted */
+ uint32_t events_discarded;
+#else
+ unsigned long events_discarded;
+#endif
+ uint32_t cpu_id;
+};
+
+struct lttng_tracedata_header {
+ struct lttng_packet_header ph;
+ struct lttng_packet_context pc;
+};
+
+/* Metadata format string */
+static const char metadata_fmt[] =
+"/* CTF 1.8 */\n"
+"typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n"
+"typealias integer { size = 16; align = 8; signed = false; } := uint16_t;\n"
+"typealias integer { size = 32; align = 8; signed = false; } := uint32_t;\n"
+"typealias integer { size = 64; align = 8; signed = false; } := uint64_t;\n"
+"typealias integer { size = 64; align = 8; signed = false; } := unsigned long;\n"
+"typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n"
+"typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n"
+"\n"
+"trace {\n"
+" major = 1;\n"
+" minor = 8;\n"
+" uuid = \"%s\";\n" /* UUID */
+" byte_order = %s;\n" /* be or le */
+" packet.header := struct {\n"
+" uint32_t magic;\n"
+" uint8_t uuid[16];\n"
+" uint32_t stream_id;\n"
+" };\n"
+"};\n"
+"\n"
+"env {\n"
+" hostname = \"%s\";\n"
+" domain = \"kernel\";\n" /* default is kernel */
+" sysname = \"Linux\";\n"
+" kernel_release = \"%s\";\n" /* uname -r */
+" kernel_version = \"%s\";\n" /* uname -v */
+" tracer_name = \"ltt\";\n"
+" tracer_major = %u;\n"
+" tracer_minor = %u;\n"
+" tracer_patchlevel = 0;\n"
+"};\n"
+"\n"
+"clock {\n"
+" name = monotonic;\n"
+" uuid = \"2f580cb3-5650-40e7-9dde-796e33b39143\";\n"
+" description = \"Monotonic Clock\";\n"
+" freq = %llu; /* Frequency, in Hz */\n" /* frequency from ltt */
+" /* clock value offset from Epoch is: offset * (1/freq) */\n"
+" offset = %llu;\n"
+"};\n"
+"\n"
+"typealias integer {\n"
+" size = 27; align = 1; signed = false;\n"
+" map = clock.monotonic.value;\n"
+"} := uint27_clock_monotonic_t;\n"
+"\n"
+"typealias integer {\n"
+" size = 32; align = 8; signed = false;\n"
+" map = clock.monotonic.value;\n"
+"} := uint32_clock_monotonic_t;\n"
+"\n"
+"typealias integer {\n"
+" size = 64; align = 8; signed = false;\n"
+" map = clock.monotonic.value;\n"
+"} := uint64_clock_monotonic_t;\n"
+"\n"
+"struct packet_context {\n"
+" uint64_clock_monotonic_t timestamp_begin;\n"
+" uint64_clock_monotonic_t timestamp_end;\n"
+" uint64_t content_size;\n"
+" uint64_t packet_size;\n"
+#ifdef CONVERT_FROM_32BIT /* 32 bit trace is converted */
+" uint32_t events_discarded;\n"
+#else
+" unsigned long events_discarded;\n"
+#endif
+" uint32_t cpu_id;\n"
+"};\n"
+"\n"
+"struct event_header_ltt {\n"
+" uint27_clock_monotonic_t timestamp;\n"
+" enum : uint5_t { id = 0 ...28, id_size_tsc = 29, id_size = 30, id_only = 31 } id;\n"
+" variant <id> {\n"
+" struct { } id;\n"
+" struct {\n"
+" uint16_t id;\n"
+" enum : uint16_t { packet_size = 0 ... 65534, extended = 65535 } packet_size;\n"
+" variant <packet_size> {\n"
+" struct { } packet_size;\n"
+" struct {\n"
+" uint32_t packet_size;\n"
+" } extended;\n"
+" } vst;\n"
+" uint64_clock_monotonic_t timestamp;\n"
+" } id_size_tsc;\n"
+" struct {\n"
+" uint16_t id;\n"
+" enum : uint16_t { packet_size = 0 ... 65534, extended = 65535 } packet_size;\n"
+" variant <packet_size> {\n"
+" struct { } packet_size;\n"
+" struct {\n"
+" uint32_t packet_size;\n"
+" } extended;\n"
+" } vs;\n"
+" } id_size;\n"
+" struct {\n"
+" uint16_t id;\n"
+" } id_only;\n"
+" } v;\n"
+"}%s;\n"
+"\n";
+
+static int _write(int fd, const void *buf, size_t len)
+{
+ int ret;
+
+ ret = write(fd, buf, len);
+ if (ret < 0) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ if (ret != len) {
+ fprintf(stderr, "incomplete write\n");
+ exit(EXIT_FAILURE);
+ }
+ return len;
+}
+
+static int _zero(int fd, size_t pad_len)
+{
+ int len, bytes = 0;
+
+ while (pad_len > 0) {
+ len = pad_len;
+ if (len > PAGE_SIZE)
+ len = PAGE_SIZE;
+ _write(fd, zero_page, len);
+ pad_len -= len;
+ bytes += len;
+ }
+ return bytes;
+}
+
+int write_event_format(char *p, char trace_size, char c_size,
+ enum ltt_type c_type, char base, char *name)
+{
+ char *start = p;
+ int align = (trace_size > ltt_hdr.alignment) ? ltt_hdr.alignment : trace_size;
+ char *fix;
+
+ /*
+ * Userspace header can contain alignmenent = 0. Work around since
+ * alignment of 0 is not allowed by CTF
+ */
+ if (!align)
+ align = 1;
+
+ /* userspace ltt can mess up the name by adding a ":", remove that */
+ fix = rindex(name, ':');
+ if (fix)
+ *fix = 0;
+
+ /*
+ * Events may have additional description after the name like:
+ * _rw(...). Don't know how to process this with CTF so remove
+ * everything in brackets.
+ */
+ fix = index(name, '(');
+ if (fix)
+ *fix = 0;
+
+ switch (c_type) {
+ case LTT_TYPE_STRING:
+ /* string _filename; */
+ p += sprintf(p, " ");
+ p += sprintf(p, "string _%s;\n", name);
+ break;
+ case LTT_TYPE_SIGNED_INT:
+ /* integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _pid; */
+ p += sprintf(p, " ");
+ p += sprintf(p, "integer { size = %d; ", trace_size * 8);
+ p += sprintf(p, "align = %d; ", align * 8);
+ p += sprintf(p, "signed = 1; encoding = none; ");
+ p += sprintf(p, "base = %d; } _%s;\n", base, name);
+ break;
+ case LTT_TYPE_UNSIGNED_INT:
+ /* integer { size = 64; align = 8; signed = 0; encoding = none; base = 16; } _start; */
+ p += sprintf(p, " ");
+ p += sprintf(p, "integer { size = %d; ", trace_size * 8);
+ p += sprintf(p, "align = %d; ", align * 8);
+ p += sprintf(p, "signed = 0; encoding = none; ");
+ p += sprintf(p, "base = %d; } _%s;\n", base, name);
+ break;
+ default:
+ printf("BUG\n");
+ }
+ return p - start;
+}
+
+static int map_file(int fd)
+{
+ struct stat sbuf;
+ int ret;
+
+ ret = fstat(fd, &sbuf);
+ if (ret < 0) {
+ perror("fstat");
+ return ret;
+ }
+
+ map_len = sbuf.st_size;
+ map = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ perror("mmap");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void align_pos(int *pos, int align)
+{
+ if (!align)
+ return;
+ if (*pos & (align - 1))
+ *pos += align - (*pos & (align - 1));
+}
+
+static int read_trace_header(struct ltt_subbuffer_header *hdr, void *addr)
+{
+ memcpy(hdr, addr, sizeof(struct ltt_subbuffer_header));
+ printf_verbose("Start TSC: %llx Stop TSC: %llx\n",
+ (unsigned long long) hdr->cycle_count_begin,
+ (unsigned long long) hdr->cycle_count_end);
+ printf_verbose("Start sec: %llx Start usex: %llx\n",
+ (unsigned long long) hdr->start_time_sec,
+ (unsigned long long) hdr->start_time_usec);
+ printf_verbose("Start freq: %llx Freq_scale: %lx\n",
+ (unsigned long long) hdr->start_freq,
+ (unsigned long) hdr->freq_scale);
+ return sizeof(struct ltt_subbuffer_header);
+}
+
+static int parse_var_event_header(struct ltt_event_header *eh, void *addr)
+{
+ int len = sizeof(uint32_t);
+
+ eh->packed = *(uint32_t *) addr;
+ addr += sizeof(eh->packed);
+ eh->timestamp = eh->packed & TSC_MASK;
+ eh->event_id = eh->packed >> TSC_SHIFT;
+
+ switch (eh->event_id) {
+ case 29:
+ eh->event_id = *(uint16_t *) addr;
+ addr += sizeof(eh->event_id);
+ len += sizeof(eh->event_id);
+ eh->event_size = *(uint16_t *) addr;
+ addr += sizeof(eh->event_size);
+ len += sizeof(eh->event_size);
+ if (eh->event_size == 0xffff) {
+ eh->packet_size = *(uint32_t *) addr;
+ addr += sizeof(eh->packet_size);
+ len += sizeof(eh->packet_size);
+ }
+ eh->timestamp = *(uint64_t *) addr;
+ addr += sizeof(eh->timestamp);
+ len += sizeof(eh->timestamp);
+ break;
+ case 30:
+ eh->event_id = *(uint16_t *) addr;
+ addr += sizeof(eh->event_id);
+ len += sizeof(eh->event_id);
+ eh->event_size = *(uint16_t *) addr;
+ addr += sizeof(eh->event_size);
+ len += sizeof(eh->event_size);
+ if (eh->event_size == 0xffff) {
+ eh->packet_size = *(uint32_t *) addr;
+ addr += sizeof(eh->packet_size);
+ len += sizeof(eh->packet_size);
+ }
+ break;
+ case 31:
+ eh->event_id = *(uint16_t *) addr;
+ addr += sizeof(eh->event_id);
+ len += sizeof(eh->event_id);
+ break;
+ }
+ return len;
+}
+
+static int read_event_header(struct ltt_event_header *eh, void *addr)
+{
+ memset(eh, 0, sizeof(*eh));
+ return parse_var_event_header(eh, addr);
+}
+
+static int is_empty_event_header(struct ltt_event_header *eh)
+{
+ struct ltt_event_header zero_eh;
+
+ memset(&zero_eh, 0, sizeof(zero_eh));
+ if (memcmp(eh, &zero_eh, sizeof(*eh)) == 0)
+ return 1;
+ else
+ return 0;
+}
+static int write_tracedata_header(int fd, uint32_t csize, uint32_t psize, int cpu)
+{
+ struct lttng_tracedata_header th;
+ ENTRY e, *eptr;
+
+ /* find stream id */
+ e.key = strdup(current_channel);
+ eptr = hsearch(e, FIND);
+ if (!eptr)
+ fprintf(stderr, "error: channel name not found\n");
+
+ memset(&th, 0, sizeof(th));
+ th.ph.magic = CTF_TRACEDATA_MAGIC;
+ memcpy(&th.ph.uuid, s_uuid, BABELTRACE_UUID_LEN);
+ th.ph.stream_id = (int) (unsigned long) eptr->data;
+
+ if (csize) /* in bits... */
+ th.pc.content_size = csize * 8;
+ else
+ th.pc.content_size = CTF_PACKET_SIZE * 8;
+ if (psize) /* in bits... */
+ th.pc.packet_size = psize * 8;
+ else
+ th.pc.packet_size = CTF_PACKET_SIZE * 8;
+
+ /* copy timestamps */
+ th.pc.timestamp_begin = ltt_hdr.cycle_count_begin;
+ th.pc.timestamp_end = ltt_hdr.cycle_count_end;
+
+ th.pc.events_discarded = ltt_hdr.events_lost;
+ th.pc.cpu_id = cpu;
+
+ return _write(fd, &th, sizeof(th));
+}
+
+static void print_event_header(struct ltt_event_header *eh)
+{
+ printf_verbose("Event header type: %d\n", eh->packed >> TSC_SHIFT);
+ printf_verbose(" timestamp short: %lx\n", eh->packed & TSC_MASK);
+ printf_verbose(" event id: %d\n", eh->event_id);
+ printf_verbose(" event size: %d\n", eh->event_size);
+ printf_verbose(" timestamp: %llx\n", (unsigned long long) eh->timestamp);
+ printf_verbose("\n");
+}
+
+static int copy_event_data(int fd, struct ltt_subbuffer_header *lh,
+ void *addr, int cpu, int old_hdr_len)
+{
+ int new_hdr_len = sizeof(struct lttng_tracedata_header);
+ uint32_t psize, dsize, esize;
+ size_t pad_len;
+ off_t pos;
+
+ /* size must be adjusted to fit the new header size */
+ esize = lh->sb_size - old_hdr_len; /* includes padding, just copy it over to avoid manual padding */
+ dsize = lh->data_size - old_hdr_len + new_hdr_len;
+
+ psize = lh->sb_size - old_hdr_len + new_hdr_len;
+ printf_debug("new psize: %x\n", psize);
+ psize = (psize + CTF_PACKET_SIZE) & ~CTF_PACKET_MASK;
+ printf_debug("aligned psize: %x\n", psize);
+
+ printf_debug("trace_header: packet_size: %x data_size: %x event size: %x\n", psize, dsize, esize);
+ write_tracedata_header(fd, dsize, psize, cpu);
+
+ /* copy event data from the input file */
+ _write(fd, addr, esize);
+ addr += esize;
+ printf_debug("copied 0x%lx data bytes\n", (unsigned long) esize);
+
+ /* now look where we are and zero pad until packet_size */
+ pos = lseek(fd, 0, SEEK_CUR);
+ printf_debug("events copied up to 0x%lx\n", pos);
+
+ pad_len = CTF_PACKET_SIZE - (pos & CTF_PACKET_MASK);
+ printf_debug("starting next header at: 0x%lx padding size: %zu\n", pos + pad_len, pad_len);
+ _zero(fd, pad_len);
+ return esize;
+}
+
+static int get_cpu_from_filename(const char *fname)
+{
+ char *start = rindex(fname, '_') + 1;
+
+ if (!start)
+ fprintf(stderr, "malformed file name: %s\n", fname);
+ return atoi(start);
+}
+
+static void set_current_channel_name(const char *fname)
+{
+ char *end = rindex(fname, '_');
+ int len;
+
+ if (!end)
+ fprintf(stderr, "malformed file name\n");
+ len = end - fname;
+
+ memset(current_channel, 0, MAX_CHANNEL_NAME);
+ strncpy(current_channel, fname, len);
+ printf_debug("set current name to: %s\n", current_channel);
+}
+
+static int parse_data_file(int in_dir_fd, char *src, char *dst, char *fname)
+{
+ int ret, in_fd, out_fd, cpu, events = 0;
+ struct ltt_event_header eh;
+ size_t pad_len;
+ void *addr;
+
+ /* skip special unrelated file */
+ if (strcmp("wrsv.filelist", fname) == 0)
+ return 0;
+
+ in_fd = openat(in_dir_fd, src, O_RDONLY);
+ if (in_fd < 0) {
+ perror("openat");
+ return -ENOENT;
+ }
+
+ ret = map_file(in_fd);
+ if (ret < 0)
+ goto out_map;
+
+ addr = map;
+ cpu = get_cpu_from_filename(fname);
+ set_current_channel_name(fname);
+
+ out_fd = open(dst, O_RDWR|O_CREAT,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+ if (out_fd < 0) {
+ perror("open");
+ ret = out_fd;
+ goto out_open;
+ }
+
+ while (1) {
+ void *old_hdr_start = addr;
+
+ if (addr - map > map_len || addr + sizeof(ltt_hdr) - map > map_len)
+ break;
+
+ addr += read_trace_header(<t_hdr, addr);
+
+ read_event_header(&eh, addr);
+ if (is_empty_event_header(&eh))
+ break;
+ print_event_header(&eh);
+ events++;
+
+ addr += copy_event_data(out_fd, <t_hdr, addr, cpu, addr - old_hdr_start);
+ }
+
+ /* no events in the trace file -> get rid of it */
+ if (!events) {
+ close(out_fd);
+ unlink(dst);
+ printf("Skipping empty data file %s\n", src);
+ goto out_open;
+ } else
+ printf("Converting data file %s -> %s\n", src, dst);
+
+ /* the final header */
+ write_tracedata_header(out_fd, sizeof(struct lttng_tracedata_header), 0, cpu);
+ /* pad header page with zeros */
+ pad_len = CTF_PACKET_SIZE - sizeof(struct lttng_tracedata_header);
+ _zero(out_fd, pad_len);
+
+ close(out_fd);
+out_open:
+ ret = munmap(map, map_len);
+ if (ret)
+ perror("munmap");
+out_map:
+ close(in_fd);
+ return ret;
+}
+
+static void parse_data_files(int in_dir_fd, DIR *in_dir, DIR *out_dir)
+{
+ char in_path[PATH_MAX], out_path[PATH_MAX];
+ struct dirent *dentry;
+ struct stat sbuf;
+ int len, ret;
+
+ while ((dentry = readdir(in_dir)) != NULL) {
+ if (dentry->d_name[0] == '.')
+ continue;
+ len = strlen(dentry->d_name);
+ if (!len)
+ continue;
+ /* don't copy metadata files! */
+ if (len >= 8 && !strncmp(dentry->d_name, "metadata", 8))
+ continue;
+
+ strcpy(in_path, s_inputname);
+ strcpy(in_path + strlen(in_path), "/");
+ strcpy(in_path + strlen(in_path), dentry->d_name);
+ strcpy(out_path, s_outputname);
+ strcpy(out_path + strlen(out_path), "/");
+ strcpy(out_path + strlen(out_path), dentry->d_name);
+
+ ret = stat(in_path, &sbuf);
+ if (ret) {
+ perror("stat");
+ continue;
+ }
+
+ if (S_ISREG(sbuf.st_mode))
+ parse_data_file(in_dir_fd, in_path, out_path, dentry->d_name);
+ }
+}
+
+static void write_stream_metadata(DIR *in_dir, int fd)
+{
+ int len, dist, ret, i, streams, cpus = 0, files = 0;
+ struct dirent *dentry;
+ struct stat sbuf;
+ char fname[PATH_MAX];
+ char buf[8 * PAGE_SIZE];
+ char *p = buf;
+ char *pos;
+
+ /*
+ * We need to declare the streams _before_ we parse the events from
+ * all files. nr_streams is still zero so we need another way to detect
+ * the number of streams...
+ */
+ memset(fname, 0, sizeof(fname));
+ sprintf(fname, "%s/metadata_0", s_inputname);
+ pos = rindex(fname, '_');
+
+ while (42) {
+ ret = stat(fname, &sbuf);
+ if (ret < 0)
+ break;
+ cpus++;
+ sprintf(pos + 1, "%d", cpus);
+ }
+
+ /* count the number of regular input files wo. special files */
+ while ((dentry = readdir(in_dir)) != NULL) {
+ memset(fname, 0, sizeof(fname));
+ if (dentry->d_name[0] == '.')
+ continue;
+
+ len = strlen(dentry->d_name);
+ if (strcmp(dentry->d_name, "wrsv.filelist") == 0)
+ continue;
+
+ strcpy(fname, s_inputname);
+ strcpy(fname + strlen(fname), "/");
+ strcpy(fname + strlen(fname), dentry->d_name);
+
+ ret = stat(fname, &sbuf);
+ if (ret) {
+ perror("stat");
+ continue;
+ }
+
+ if (S_ISREG(sbuf.st_mode))
+ files++;
+ }
+ /* reset dir stream position to beginning for subsequent use */
+ rewinddir(in_dir);
+
+ printf_debug("cpus: %d files: %d\n", cpus, files);
+ streams = files / cpus;
+ for (i = 0; i < streams; i++) {
+ p += sprintf(p, "stream {\n");
+ p += sprintf(p, " id = %u;\n", i);
+ p += sprintf(p, " event.header := struct event_header_ltt;\n");
+ p += sprintf(p, " packet.context := struct packet_context;\n");
+ p += sprintf(p, "};\n");
+ p += sprintf(p, "\n");
+ }
+
+ /* get len of event data */
+ len = p - buf;
+
+ /* now check if we cross the packet_size border */
+ dist = written % CTF_PACKET_SIZE;
+ dist = CTF_PACKET_SIZE - dist;
+ if (dist >= len) {
+ written += _write(fd, buf, len);
+ return;
+ }
+
+ /* not enough space until border */
+ written += _write(fd, buf, dist);
+
+ /* rest of event data */
+ written += _write(fd, buf + dist, len - dist);
+}
+
+static void write_event_metadata(struct ltt_event_info *ec, int fd)
+{
+ char buf[PAGE_SIZE]; /* should be enough for any event */
+ char *p = buf;
+ ENTRY e, *eptr;
+ int len, dist;
+
+ /* find stream id */
+ e.key = strdup(ec->channel_name);
+ eptr = hsearch(e, FIND);
+ if (!eptr)
+ fprintf(stderr, "error: channel name not found\n");
+
+ p += sprintf(p, "event {\n");
+ p += sprintf(p, " name = %s_%s;\n", ec->channel_name, ec->event_name);
+ p += sprintf(p, " id = %u;\n", ec->event_id);
+ p += sprintf(p, " stream_id = %u;\n", (int) (unsigned long) eptr->data);
+ p += sprintf(p, " fields := struct {\n");
+
+ if (ec->format)
+ p += parse_format(p, ec->format, ltt_hdr.arch_size, ltt_size_long,
+ ltt_size_size_t, write_event_format);
+ p += sprintf(p, " };\n");
+ p += sprintf(p, "};\n");
+ p += sprintf(p, "\n");
+
+ /* get len of event data */
+ len = p - buf;
+
+ /* now check if we cross the packet_size border */
+ dist = written % CTF_PACKET_SIZE;
+ dist = CTF_PACKET_SIZE - dist;
+ if (dist >= len) {
+ written += _write(fd, buf, len);
+ return;
+ }
+
+ /* not enough space until border */
+ written += _write(fd, buf, dist);
+
+ /* rest of event data */
+ written += _write(fd, buf + dist, len - dist);
+}
+
+static void *parse_events(void *addr, int out_fd)
+{
+ int len, pos = 0;
+ struct ltt_event_header eh;
+ struct ltt_event_info *ec;
+ ENTRY e, *eptr;
+
+ printf_debug("reading ehdr from: %p\n", addr);
+ pos += read_event_header(&eh, addr);
+
+ if (is_empty_event_header(&eh))
+ return NULL;
+ print_event_header(&eh);
+
+ ec = malloc(sizeof(*ec));
+ if (!ec) {
+ perror("malloc");
+ return NULL;
+ }
+ memset(ec, 0, sizeof(*ec));
+
+ /* channel name string */
+ len = strlen(addr + pos);
+ if (!len)
+ return NULL;
+
+ ec->channel_name = malloc(len + 1);
+ memset(ec->channel_name, 0, len + 1);
+ strcpy(ec->channel_name, addr + pos);
+ pos += len + 1;
+
+ /* try to add to stream hash */
+ e.key = strdup(ec->channel_name);
+
+ eptr = hsearch(e, FIND);
+ if (!eptr) {
+ /* not yet cached, add it */
+ e.data = (void *) (unsigned long) (nr_streams++);
+ eptr = hsearch(e, ENTER);
+ if (!eptr)
+ fprintf(stderr, "hash table full\n");
+ printf_debug("hashing channel: %s -> stream_id: %u\n",
+ eptr ? e.key : NULL, eptr ? (int) (unsigned long) (e.data) : 0);
+ } else {
+ /* nothing to do */
+ printf_debug("channel: %s already hashed to stream_id %u\n", e.key, (int) (unsigned long) eptr->data);
+ free(e.key);
+ }
+
+ /* event name string */
+ len = strlen(addr + pos);
+ if (!len)
+ return NULL;
+
+ ec->event_name = malloc(len + 1);
+ memset(ec->event_name, 0, len + 1);
+ strcpy(ec->event_name, addr + pos);
+ pos += len + 1;
+ printf_debug("event: %s\n", ec->event_name);
+ nr_events++;
+
+ /* event id */
+ if (ltt_hdr.alignment)
+ align_pos(&pos, sizeof(uint16_t));
+ ec->event_id = *(uint16_t *) (addr + pos);
+ printf_verbose("Event channel: %s name: %s id: %d\n",
+ ec->channel_name, ec->event_name, ec->event_id);
+ pos += 2;
+
+ /* size values */
+ ltt_size_int = *(uint8_t *) (addr + pos);
+ pos++;
+ ltt_size_long = *(uint8_t *) (addr + pos);
+ pos++;
+ ltt_size_ptr = *(uint8_t *) (addr + pos);
+ pos++;
+ ltt_size_size_t = *(uint8_t *) (addr + pos);
+ pos++;
+
+ /* get align value */
+ ec->align = *(uint8_t *) (addr + pos);
+ pos++;
+
+ align_pos(&pos, ec->align);
+
+ pos += parse_var_event_header(&eh, addr + pos);
+ print_event_header(&eh);
+
+ /* skip channel string repeated */
+ pos += strlen(ec->channel_name) + 1;
+
+ /* skip event string repeated */
+ pos += strlen(ec->event_name) + 1;
+
+ /* format string, empty strings are possible! */
+ len = strlen(addr + pos);
+
+ if (len) {
+ ec->format = malloc(len + 1);
+ memset(ec->format, 0, len + 1);
+ strcpy(ec->format, addr + pos);
+ }
+ printf_debug("format: %s\n", ec->format);
+ pos += len + 1;
+
+ align_pos(&pos, ec->align);
+
+ /* append event description to metadata */
+ write_event_metadata(ec, out_fd);
+
+ if (len)
+ free(ec->format);
+ free(ec->event_name);
+ free(ec->channel_name);
+ free(ec);
+ return addr + pos;
+}
+
+static int read_trace_header_fd(int in_dir_fd, char *name)
+{
+ int ret, fd;
+
+ fd = openat(in_dir_fd, name, O_RDONLY);
+ if (fd < 0) {
+ perror("openat");
+ return fd;
+ }
+
+ ret = map_file(fd);
+ if (ret < 0)
+ goto out;
+ memcpy(<t_hdr, map, sizeof(struct ltt_subbuffer_header));
+
+ ret = munmap(map, map_len);
+ if (ret)
+ perror("munmap");
+out:
+ close(fd);
+ return ret;
+}
+
+static int parse_input_metadata(int in_dir_fd, int out_fd)
+{
+ int ret, fd, nr = 0;
+ char name[12];
+ void *addr;
+
+next:
+ memset(name, 0, sizeof(name));
+ sprintf(name, "metadata_%d", nr);
+
+ fd = openat(in_dir_fd, name, O_RDONLY);
+ if (fd < 0)
+ /* only an error for metadata_0 which is checked before */
+ return -ENOENT;
+
+ ret = map_file(fd);
+ if (ret < 0) {
+ close(fd);
+ return 0;
+ }
+
+ addr = map;
+ addr += read_trace_header(<t_hdr, addr);
+
+ printf("Parsing metadata from file %s\n", name);
+ printf_verbose("Version %d:%d\n", ltt_hdr.major_version, ltt_hdr.minor_version);
+ printf_verbose("Start TSC: %llx Stop TSC: %llx\n",
+ (unsigned long long) ltt_hdr.cycle_count_begin,
+ (unsigned long long) ltt_hdr.cycle_count_end);
+
+ /* now fetch the events */
+ do {
+ addr = parse_events(addr, out_fd);
+ } while (addr);
+
+ ret = munmap(map, map_len);
+ if (ret)
+ perror("munmap");
+ ret = close(fd);
+ if (ret)
+ perror("close");
+ nr++;
+ goto next;
+}
+
+static void print_metadata(FILE *fp)
+{
+ char uuid_str[BABELTRACE_UUID_STR_LEN];
+ unsigned int major = 0, minor = 0;
+ int le, ret;
+
+ if (ltt_hdr.magic_number == LTT_MAGIC_NUMBER_LE)
+ le = 1;
+ else if (ltt_hdr.magic_number == LTT_MAGIC_NUMBER_BE)
+ le = 0;
+ else {
+ fprintf(stderr, "failed to parse ltt magic number\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = sscanf(VERSION, "%u.%u", &major, &minor);
+ if (ret != 2)
+ fprintf(stderr, "[warning] Incorrect babeltrace version format\n.");
+ babeltrace_uuid_unparse(s_uuid, uuid_str);
+ fprintf(fp, metadata_fmt,
+ uuid_str,
+ le ? "le" : "be",
+ "none",
+ "3.4",
+ "unknown",
+ ltt_hdr.major_version,
+ ltt_hdr.minor_version,
+ (unsigned long long) ltt_hdr.start_freq,
+ (unsigned long long) ltt_hdr.start_time_sec * 1000000000 + ltt_hdr.start_time_usec,
+ ltt_hdr.alignment ? " align(32)" : "");
+}
+
+static void usage(FILE *fp)
+{
+ fprintf(fp, "BabelTrace Legacy Converter %s\n", VERSION);
+ fprintf(fp, "\n");
+ fprintf(fp, "Convert from LTT 2.6 to CTF 1.8 format.\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "usage : babeltrace-legacy [OPTIONS] INPUT OUTPUT\n");
+ fprintf(fp, "\n");
+ fprintf(fp, " INPUT Input trace path\n");
+ fprintf(fp, "\n");
+ fprintf(fp, " OUTPUT Output trace path\n");
+ fprintf(fp, "\n");
+ fprintf(fp, " -d Debug mode\n");
+ fprintf(fp, "\n");
+ fprintf(fp, " -v Verbose mode\n");
+ fprintf(fp, "\n");
+}
+
+static int parse_args(int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-h")) {
+ s_help = 1;
+ return 0;
+ } else if (!strcmp(argv[i], "-d")) {
+ babeltrace_debug = 1;
+ } else if (!strcmp(argv[i], "-v")) {
+ babeltrace_verbose = 1;
+ } else if (argv[i][0] == '-') {
+ return -EINVAL;
+ } else if (!s_inputname) {
+ s_inputname = argv[i];
+ } else if (!s_outputname) {
+ s_outputname = argv[i];
+ }
+ }
+ if (!s_inputname || !s_outputname)
+ return -EINVAL;
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int out_metadata_fd, ret;
+ DIR *in_dir, *out_dir;
+ int in_dir_fd, out_dir_fd;
+ FILE *out_metadata_fp;
+ off_t pos;
+
+ ret = parse_args(argc, argv);
+ if (ret) {
+ fprintf(stderr, "Error: invalid argument.\n");
+ usage(stderr);
+ goto error;
+ }
+
+ if (s_help) {
+ usage(stdout);
+ exit(EXIT_SUCCESS);
+ }
+
+ babeltrace_uuid_generate(s_uuid);
+
+ hcreate(MAX_NR_STREAMS);
+
+ in_dir = opendir(s_inputname);
+ if (!in_dir) {
+ perror("opendir");
+ goto error;
+ }
+ in_dir_fd = dirfd(in_dir);
+ if (in_dir_fd < 0) {
+ perror("dirfd");
+ goto error_closedirin;
+ }
+
+ ret = mkdir(s_outputname, S_IRWXU|S_IRWXG);
+ if (ret) {
+ perror("mkdir");
+ goto error_mkdir;
+ }
+
+ out_dir = opendir(s_outputname);
+ if (!out_dir) {
+ perror("opendir");
+ goto error_rmdir;
+ }
+ out_dir_fd = dirfd(out_dir);
+ if (out_dir_fd < 0) {
+ perror("dirfd");
+ goto error_closedir;
+ }
+
+ out_metadata_fd = openat(out_dir_fd, "metadata", O_RDWR|O_CREAT,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+ if (out_metadata_fd < 0) {
+ perror("openat");
+ goto error_closedatastream;
+ }
+
+ out_metadata_fp = fdopen(out_metadata_fd, "aw");
+ if (!out_metadata_fp) {
+ perror("fdopen");
+ goto error_closemetadatafd;
+ }
+
+ read_trace_header_fd(in_dir_fd, "metadata_0");
+
+ print_metadata(out_metadata_fp);
+ fflush(out_metadata_fp);
+
+ lseek(out_metadata_fd, 0 , SEEK_SET);
+ pos = lseek(out_metadata_fd, 0, SEEK_END); /* make sure writing to out_metadata_fd goes to the end */
+ if (pos < 0)
+ perror("lseek");
+ /* reset written to pos otherwise we miss the metadata_fmt fprint */
+ written = pos;
+
+ write_stream_metadata(in_dir, out_metadata_fd);
+ parse_input_metadata(in_dir_fd, out_metadata_fd);
+
+ /* data files */
+ parse_data_files(in_dir_fd, in_dir, out_dir);
+
+ printf("\nTotal events converted: %d\n", nr_events);
+ hdestroy();
+ exit(EXIT_SUCCESS);
+
+error_closemetadatafd:
+ close(out_metadata_fd);
+error_closedatastream:
+ close(out_dir_fd);
+error_closedir:
+ closedir(out_dir);
+error_rmdir:
+ ret = rmdir(s_outputname);
+ if (ret)
+ perror("rmdir");
+error_mkdir:
+ close(in_dir_fd);
+error_closedirin:
+ closedir(in_dir);
+error:
+ hdestroy();
+ exit(EXIT_FAILURE);
+}
--
1.7.9.5
More information about the lttng-dev
mailing list