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 <[email protected]> --- 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 <[email protected]> + * + * 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 _______________________________________________ lttng-dev mailing list [email protected] http://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev
