static void usage(FILE *fp)
{
- fprintf(fp, "Babeltrace %u.%u\n\n", BABELTRACE_VERSION_MAJOR,
+ fprintf(fp, "BabelTrace Trace Converter %u.%u\n\n",
+ BABELTRACE_VERSION_MAJOR,
BABELTRACE_VERSION_MINOR);
fprintf(fp, "usage : babeltrace [OPTIONS] INPUT OUTPUT\n");
fprintf(fp, "\n");
poptContext pc;
int opt, ret = 0;
+ if (argc == 1) {
+ usage(stdout);
+ return 1; /* exit cleanly */
+ }
+
pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
poptReadDefaultConfig(pc, 0);
#include <babeltrace/ctf/types.h>
#include <babeltrace/ctf/metadata.h>
#include <babeltrace/babeltrace.h>
+#include <inttypes.h>
+#include <uuid/uuid.h>
+#include <sys/mman.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
-
#include "metadata/ctf-scanner.h"
#include "metadata/ctf-parser.h"
#include "metadata/ctf-ast.h"
+/*
+ * We currently simply map a page to read the packet header and packet
+ * context to get the packet length and content length.
+ */
+#define MAX_PACKET_HEADER_LEN getpagesize()
+#define UUID_LEN 16 /* uuid by value len */
+
extern int yydebug;
struct trace_descriptor {
.close_trace = ctf_close_trace,
};
+void move_pos_slow(struct stream_pos *pos, size_t offset)
+{
+ int ret;
+
+ /*
+ * The caller should never ask for move_pos across packets,
+ * except to get exactly at the beginning of the next packet.
+ */
+ assert(pos->offset + offset == pos->content_size);
+
+ if (pos->base) {
+ /* unmap old base */
+ ret = munmap(pos->base, pos->packet_size);
+ if (ret) {
+ fprintf(stdout, "Unable to unmap old base: %s.\n",
+ strerror(errno));
+ assert(0);
+ }
+ }
+
+ pos->mmap_offset += pos->packet_size / CHAR_BIT;
+ /* map new base. Need mapping length from header. */
+ pos->base = mmap(NULL, MAX_PACKET_HEADER_LEN, PROT_READ,
+ MAP_PRIVATE, pos->fd, pos->mmap_offset);
+ pos->content_size = 0; /* Unknown at this point */
+ pos->packet_size = 0; /* Unknown at this point */
+
+}
+
/*
* TODO: for now, we treat the metadata file as a simple text file
* (without any header nor packets nor padding).
FILE *fp;
int ret = 0;
- td->ctf_trace.metadata.fd = openat(td->ctf_trace.dirfd,
+ td->ctf_trace.metadata.pos.fd = openat(td->ctf_trace.dirfd,
"metadata", O_RDONLY);
- if (td->ctf_trace.metadata.fd < 0) {
+ if (td->ctf_trace.metadata.pos.fd < 0) {
fprintf(stdout, "Unable to open metadata.\n");
- return td->ctf_trace.metadata.fd;
+ return td->ctf_trace.metadata.pos.fd;
}
if (babeltrace_debug)
yydebug = 1;
- fp = fdopen(td->ctf_trace.metadata.fd, "r");
+ fp = fdopen(td->ctf_trace.metadata.pos.fd, "r");
if (!fp) {
fprintf(stdout, "Unable to open metadata stream.\n");
ret = -errno;
end_scanner_alloc:
fclose(fp);
end_stream:
- close(td->ctf_trace.metadata.fd);
+ close(td->ctf_trace.metadata.pos.fd);
+ return ret;
+}
+
+
+static
+int create_stream_packet_index(struct trace_descriptor *td,
+ struct ctf_file_stream *file_stream)
+{
+ struct ctf_stream *stream;
+ int len_index;
+ struct stream_pos *pos;
+ struct stat filestats;
+ struct packet_index packet_index;
+ int first_packet = 1;
+ int ret;
+
+ pos = &file_stream->pos;
+
+ ret = fstat(pos->fd, &filestats);
+ if (ret < 0)
+ return ret;
+
+ for (pos->mmap_offset = 0; pos->mmap_offset < filestats.st_size; ) {
+ uint64_t stream_id = 0;
+
+ if (pos->base) {
+ /* unmap old base */
+ ret = munmap(pos->base, pos->packet_size);
+ if (ret) {
+ fprintf(stdout, "Unable to unmap old base: %s.\n",
+ strerror(errno));
+ return ret;
+ }
+ }
+ /* map new base. Need mapping length from header. */
+ pos->base = mmap(NULL, MAX_PACKET_HEADER_LEN, PROT_READ,
+ MAP_PRIVATE, pos->fd, pos->mmap_offset);
+ pos->content_size = 0; /* Unknown at this point */
+ pos->packet_size = 0; /* Unknown at this point */
+ pos->offset = 0; /* Position of the packet header */
+
+ /* read and check header, set stream id (and check) */
+ if (td->ctf_trace.packet_header) {
+ /* Read packet header */
+ td->ctf_trace.packet_header->p.declaration->copy(NULL, NULL,
+ pos, &ctf_format, &td->ctf_trace.packet_header->p);
+
+ len_index = struct_declaration_lookup_field_index(td->ctf_trace.packet_header->declaration, g_quark_from_static_string("magic"));
+ if (len_index >= 0) {
+ struct definition_integer *defint;
+ struct field *field;
+
+ field = struct_definition_get_field_from_index(td->ctf_trace.packet_header, len_index);
+ assert(field->definition->declaration->id == CTF_TYPE_INTEGER);
+ defint = container_of(field->definition, struct definition_integer, p);
+ assert(defint->declaration->signedness == FALSE);
+ if (defint->value._unsigned != CTF_MAGIC) {
+ fprintf(stdout, "[error] Invalid magic number %" PRIX64 ".\n",
+ defint->value._unsigned);
+ return -EINVAL;
+ }
+ }
+
+ /* check uuid */
+ len_index = struct_declaration_lookup_field_index(td->ctf_trace.packet_header->declaration, g_quark_from_static_string("trace_uuid"));
+ if (len_index >= 0) {
+ struct definition_array *defarray;
+ struct field *field;
+ uint64_t i;
+ uint8_t uuidval[UUID_LEN];
+
+
+ field = struct_definition_get_field_from_index(td->ctf_trace.packet_header, len_index);
+ assert(field->definition->declaration->id == CTF_TYPE_ARRAY);
+ defarray = container_of(field->definition, struct definition_array, p);
+ assert(defarray->declaration->len == UUID_LEN);
+ assert(defarray->declaration->elem->id == CTF_TYPE_INTEGER);
+
+ for (i = 0; i < UUID_LEN; i++) {
+ struct definition *elem;
+ struct definition_integer *defint;
+
+ elem = array_index(defarray, i);
+ assert(elem);
+ defint = container_of(elem, struct definition_integer, p);
+ uuidval[i] = defint->value._unsigned;
+ }
+ ret = uuid_compare(td->ctf_trace.uuid, uuidval);
+ if (ret) {
+ fprintf(stdout, "[error] Unique Universal Identifiers do not match.\n");
+ return -EINVAL;
+ }
+ }
+
+
+ len_index = struct_declaration_lookup_field_index(td->ctf_trace.packet_header->declaration, g_quark_from_static_string("stream_id"));
+ if (len_index >= 0) {
+ struct definition_integer *defint;
+ struct field *field;
+
+ field = struct_definition_get_field_from_index(td->ctf_trace.packet_header, len_index);
+ assert(field->definition->declaration->id == CTF_TYPE_INTEGER);
+ defint = container_of(field->definition, struct definition_integer, p);
+ assert(defint->declaration->signedness == FALSE);
+ stream_id = defint->value._unsigned;
+ }
+ }
+
+ if (!first_packet && file_stream->stream_id != stream_id) {
+ fprintf(stdout, "[error] Stream ID is changing within a stream.\n");
+ return -EINVAL;
+ }
+ if (first_packet) {
+ file_stream->stream_id = stream_id;
+ if (stream_id >= td->ctf_trace.streams->len) {
+ fprintf(stdout, "[error] Stream %" PRIu64 " is not declared in metadata.\n", stream_id);
+ return -EINVAL;
+ }
+ stream = g_ptr_array_index(td->ctf_trace.streams, stream_id);
+ if (!stream) {
+ fprintf(stdout, "[error] Stream %" PRIu64 " is not declared in metadata.\n", stream_id);
+ return -EINVAL;
+ }
+ file_stream->stream = stream;
+ }
+ first_packet = 0;
+
+ /* Read packet context */
+ stream->packet_context->p.declaration->copy(NULL, NULL,
+ pos, &ctf_format, &stream->packet_context->p);
+
+ /* read content size from header */
+ len_index = struct_declaration_lookup_field_index(stream->packet_context->declaration, g_quark_from_static_string("content_size"));
+ if (len_index >= 0) {
+ struct definition_integer *defint;
+ struct field *field;
+
+ field = struct_definition_get_field_from_index(stream->packet_context, len_index);
+ assert(field->definition->declaration->id == CTF_TYPE_INTEGER);
+ defint = container_of(field->definition, struct definition_integer, p);
+ assert(defint->declaration->signedness == FALSE);
+ pos->content_size = defint->value._unsigned;
+ } else {
+ /* Use file size for packet size */
+ pos->content_size = filestats.st_size * CHAR_BIT;
+ }
+
+ /* read packet size from header */
+ len_index = struct_declaration_lookup_field_index(stream->packet_context->declaration, g_quark_from_static_string("packet_size"));
+ if (len_index >= 0) {
+ struct definition_integer *defint;
+ struct field *field;
+
+ field = struct_definition_get_field_from_index(stream->packet_context, len_index);
+ assert(field->definition->declaration->id == CTF_TYPE_INTEGER);
+ defint = container_of(field->definition, struct definition_integer, p);
+ assert(defint->declaration->signedness == FALSE);
+ pos->packet_size = defint->value._unsigned;
+ } else {
+ /* Use content size if non-zero, else file size */
+ pos->packet_size = pos->content_size ? : filestats.st_size * CHAR_BIT;
+ }
+
+ packet_index.offset = pos->mmap_offset;
+ packet_index.content_size = pos->content_size;
+ packet_index.packet_size = pos->packet_size;
+ /* add index to packet array */
+ g_array_append_val(file_stream->pos.packet_index, packet_index);
+
+ pos->mmap_offset += pos->packet_size / CHAR_BIT;
+ }
+
+ return 0;
+}
+
+/*
+ * Note: many file streams can inherit from the same stream class
+ * description (metadata).
+ */
+static
+int ctf_open_file_stream_read(struct trace_descriptor *td, const char *path, int flags)
+{
+ int ret;
+ struct ctf_file_stream *file_stream;
+
+ ret = openat(td->ctf_trace.dirfd, path, flags);
+ if (ret < 0)
+ goto error;
+ file_stream = g_new0(struct ctf_file_stream, 1);
+ file_stream->pos.fd = ret;
+ file_stream->pos.packet_index = g_array_new(FALSE, TRUE,
+ sizeof(struct packet_index));
+ ret = create_stream_packet_index(td, file_stream);
+ if (ret)
+ goto error_index;
+ /* Add stream file to stream class */
+ g_ptr_array_add(file_stream->stream->files, file_stream);
+ return 0;
+
+error_index:
+ (void) g_array_free(file_stream->pos.packet_index, TRUE);
+ close(file_stream->pos.fd);
+ g_free(file_stream);
+error:
return ret;
}
ret = -ENOENT;
goto error_dirfd;
}
+
+ td->ctf_trace.streams = g_ptr_array_new();
+
/*
* Keep the metadata file separate.
*/
if (ret) {
fprintf(stdout, "Readdir error.\n");
goto readdir_error;
-
}
if (!diriter)
break;
|| !strcmp(diriter->d_name, "..")
|| !strcmp(diriter->d_name, "metadata"))
continue;
+ /* TODO: open file stream */
}
free(dirent);
readdir_error:
free(dirent);
error_metadata:
+ g_ptr_array_free(td->ctf_trace.streams, TRUE);
close(td->ctf_trace.dirfd);
error_dirfd:
closedir(td->ctf_trace.dir);
return NULL;
}
+static
+void ctf_close_file_stream(struct ctf_file_stream *file_stream)
+{
+ (void) g_array_free(file_stream->pos.packet_index, TRUE);
+ close(file_stream->pos.fd);
+}
+
void ctf_close_trace(struct trace_descriptor *td)
{
+ int i;
+
+ if (td->ctf_trace.streams) {
+ for (i = 0; i < td->ctf_trace.streams->len; i++) {
+ struct ctf_stream *stream;
+ int j;
+
+ stream = g_ptr_array_index(td->ctf_trace.streams, i);
+ for (j = 0; j < stream->files->len; j++) {
+ struct ctf_file_stream *file_stream;
+ file_stream = g_ptr_array_index(td->ctf_trace.streams, j);
+ ctf_close_file_stream(file_stream);
+ }
+
+ }
+ g_ptr_array_free(td->ctf_trace.streams, TRUE);
+ }
closedir(td->ctf_trace.dir);
g_free(td);
}
major = 0;
minor = 1;
uuid = "f816d884-6cea-11e0-ac7a-8f5f4e9f7724";
- endian = big; /* Assuming big endian streams */
+ byte_order = be; /* Assuming big endian streams */
};
/* Architecture with 32-bit pointers, 32-bit integers, 32-bit longs */
}
+static
+int get_trace_byte_order(FILE *fd, int depth, struct ctf_node *unary_expression)
+{
+ int byte_order;
+
+ if (unary_expression->u.unary_expression.type != UNARY_STRING) {
+ fprintf(fd, "[error] %s: byte_order: expecting string\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (!strcmp(unary_expression->u.unary_expression.u.string, "be"))
+ byte_order = BIG_ENDIAN;
+ else if (!strcmp(unary_expression->u.unary_expression.u.string, "le"))
+ byte_order = LITTLE_ENDIAN;
+ else {
+ fprintf(fd, "[error] %s: unexpected string \"%s\". Should be \"native\", \"network\", \"be\" or \"le\".\n",
+ __func__, unary_expression->u.unary_expression.u.string);
+ return -EINVAL;
+ }
+ return byte_order;
+}
+
static
int get_byte_order(FILE *fd, int depth, struct ctf_node *unary_expression,
struct ctf_trace *trace)
if (CTF_EVENT_FIELD_IS_SET(event, name)) {
fprintf(fd, "[error] %s: name already declared in event declaration\n", __func__);
- return -EPERM;
+ ret = -EPERM;
+ goto error;
}
right = concatenate_unary_strings(&node->u.ctf_expression.right);
if (!right) {
fprintf(fd, "[error] %s: unexpected unary expression for event name\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
event->name = g_quark_from_string(right);
g_free(right);
} else if (!strcmp(left, "id")) {
if (CTF_EVENT_FIELD_IS_SET(event, id)) {
fprintf(fd, "[error] %s: id already declared in event declaration\n", __func__);
- return -EPERM;
+ ret = -EPERM;
+ goto error;
}
ret = get_unary_unsigned(&node->u.ctf_expression.right, &event->id);
if (ret) {
fprintf(fd, "[error] %s: unexpected unary expression for event id\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
CTF_EVENT_SET_FIELD(event, id);
} else if (!strcmp(left, "stream_id")) {
if (CTF_EVENT_FIELD_IS_SET(event, stream_id)) {
fprintf(fd, "[error] %s: stream_id already declared in event declaration\n", __func__);
- return -EPERM;
+ ret = -EPERM;
+ goto error;
}
ret = get_unary_unsigned(&node->u.ctf_expression.right, &event->stream_id);
if (ret) {
fprintf(fd, "[error] %s: unexpected unary expression for event stream_id\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
event->stream = trace_stream_lookup(trace, event->stream_id);
if (!event->stream) {
fprintf(fd, "[error] %s: stream id %" PRIu64 " cannot be found\n", __func__, event->stream_id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
CTF_EVENT_SET_FIELD(event, stream_id);
} else if (!strcmp(left, "context")) {
if (event->context_decl) {
fprintf(fd, "[error] %s: context already declared in event declaration\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
declaration = ctf_type_specifier_list_visit(fd, depth,
_cds_list_first_entry(&node->u.ctf_expression.right,
struct ctf_node, siblings),
event->declaration_scope, trace);
- if (!declaration)
- return -EPERM;
- if (declaration->id != CTF_TYPE_STRUCT)
- return -EPERM;
+ if (!declaration) {
+ ret = -EPERM;
+ goto error;
+ }
+ if (declaration->id != CTF_TYPE_STRUCT) {
+ ret = -EPERM;
+ goto error;
+ }
event->context_decl = container_of(declaration, struct declaration_struct, p);
} else if (!strcmp(left, "fields")) {
struct declaration *declaration;
if (event->fields_decl) {
fprintf(fd, "[error] %s: fields already declared in event declaration\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
declaration = ctf_type_specifier_list_visit(fd, depth,
_cds_list_first_entry(&node->u.ctf_expression.right,
struct ctf_node, siblings),
event->declaration_scope, trace);
- if (!declaration)
- return -EPERM;
- if (declaration->id != CTF_TYPE_STRUCT)
- return -EPERM;
+ if (!declaration) {
+ ret = -EPERM;
+ goto error;
+ }
+ if (declaration->id != CTF_TYPE_STRUCT) {
+ ret = -EPERM;
+ goto error;
+ }
event->fields_decl = container_of(declaration, struct declaration_struct, p);
}
+error:
g_free(left);
break;
}
/* TODO: declaration specifier should be added. */
}
- return 0;
+ return ret;
}
static
fprintf(fd, "[error] %s: missing name field in event declaration\n", __func__);
goto error;
}
- if (!CTF_EVENT_FIELD_IS_SET(event, id)) {
+ /* Allow only one event without id per stream */
+ if (!CTF_EVENT_FIELD_IS_SET(event, id)
+ && event->stream->events_by_id->len != 0) {
ret = -EPERM;
fprintf(fd, "[error] %s: missing id field in event declaration\n", __func__);
goto error;
}
if (!CTF_EVENT_FIELD_IS_SET(event, stream_id)) {
- ret = -EPERM;
- fprintf(fd, "[error] %s: missing stream_id field in event declaration\n", __func__);
- goto error;
+ /* Allow missing stream_id if there is only a single stream */
+ if (trace->streams->len == 1) {
+ event->stream_id = 0;
+ event->stream = trace_stream_lookup(trace, event->stream_id);
+ } else {
+ ret = -EPERM;
+ fprintf(fd, "[error] %s: missing stream_id field in event declaration\n", __func__);
+ goto error;
+ }
}
if (event->stream->events_by_id->len <= event->id)
g_ptr_array_set_size(event->stream->events_by_id, event->id + 1);
if (!strcmp(left, "id")) {
if (CTF_STREAM_FIELD_IS_SET(stream, stream_id)) {
fprintf(fd, "[error] %s: id already declared in stream declaration\n", __func__);
- return -EPERM;
+ ret = -EPERM;
+ goto error;
}
ret = get_unary_unsigned(&node->u.ctf_expression.right, &stream->stream_id);
if (ret) {
fprintf(fd, "[error] %s: unexpected unary expression for stream id\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
CTF_STREAM_SET_FIELD(stream, stream_id);
} else if (!strcmp(left, "event.header")) {
if (stream->event_header_decl) {
fprintf(fd, "[error] %s: event.header already declared in stream declaration\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
declaration = ctf_type_specifier_list_visit(fd, depth,
_cds_list_first_entry(&node->u.ctf_expression.right,
struct ctf_node, siblings),
stream->declaration_scope, trace);
- if (!declaration)
- return -EPERM;
- if (declaration->id != CTF_TYPE_STRUCT)
- return -EPERM;
+ if (!declaration) {
+ ret = -EPERM;
+ goto error;
+ }
+ if (declaration->id != CTF_TYPE_STRUCT) {
+ ret = -EPERM;
+ goto error;
+ }
stream->event_header_decl = container_of(declaration, struct declaration_struct, p);
} else if (!strcmp(left, "event.context")) {
struct declaration *declaration;
if (stream->event_context_decl) {
fprintf(fd, "[error] %s: event.context already declared in stream declaration\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
declaration = ctf_type_specifier_list_visit(fd, depth,
_cds_list_first_entry(&node->u.ctf_expression.right,
struct ctf_node, siblings),
stream->declaration_scope, trace);
- if (!declaration)
- return -EPERM;
- if (declaration->id != CTF_TYPE_STRUCT)
- return -EPERM;
+ if (!declaration) {
+ ret = -EPERM;
+ goto error;
+ }
+ if (declaration->id != CTF_TYPE_STRUCT) {
+ ret = -EPERM;
+ goto error;
+ }
stream->event_context_decl = container_of(declaration, struct declaration_struct, p);
} else if (!strcmp(left, "packet.context")) {
struct declaration *declaration;
if (stream->packet_context_decl) {
fprintf(fd, "[error] %s: packet.context already declared in stream declaration\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
declaration = ctf_type_specifier_list_visit(fd, depth,
_cds_list_first_entry(&node->u.ctf_expression.right,
struct ctf_node, siblings),
stream->declaration_scope, trace);
- if (!declaration)
- return -EPERM;
- if (declaration->id != CTF_TYPE_STRUCT)
- return -EPERM;
+ if (!declaration) {
+ ret = -EPERM;
+ goto error;
+ }
+ if (declaration->id != CTF_TYPE_STRUCT) {
+ ret = -EPERM;
+ goto error;
+ }
stream->packet_context_decl = container_of(declaration, struct declaration_struct, p);
}
+error:
g_free(left);
break;
}
/* TODO: declaration specifier should be added. */
}
- return 0;
+ return ret;
}
static
stream->declaration_scope = new_declaration_scope(parent_declaration_scope);
stream->events_by_id = g_ptr_array_new();
stream->event_quark_to_id = g_hash_table_new(g_direct_hash, g_direct_equal);
+ stream->files = g_ptr_array_new();
cds_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) {
ret = ctf_stream_declaration_visit(fd, depth + 1, iter, stream, trace);
if (ret)
goto error;
}
- if (!CTF_STREAM_FIELD_IS_SET(stream, stream_id)) {
+ if (CTF_STREAM_FIELD_IS_SET(stream, stream_id)) {
+ /* check that packet header has stream_id field. */
+ if (!trace->packet_header_decl
+ || struct_declaration_lookup_field_index(trace->packet_header_decl, g_quark_from_static_string("stream_id")) < 0) {
+ ret = -EPERM;
+ fprintf(fd, "[error] %s: missing stream_id field in packet header declaration, but stream_id attribute is declared for stream.\n", __func__);
+ goto error;
+ }
+ }
+
+ /* Allow only one id-less stream */
+ if (!CTF_STREAM_FIELD_IS_SET(stream, stream_id)
+ && trace->streams->len != 0) {
ret = -EPERM;
fprintf(fd, "[error] %s: missing id field in stream declaration\n", __func__);
goto error;
g_ptr_array_set_size(trace->streams, stream->stream_id + 1);
g_ptr_array_index(trace->streams, stream->stream_id) = stream;
- parent_def_scope = NULL;
+ parent_def_scope = trace->definition_scope;
if (stream->packet_context_decl) {
stream->packet_context =
container_of(
declaration_unref(&stream->event_header_decl->p);
declaration_unref(&stream->event_context_decl->p);
declaration_unref(&stream->packet_context_decl->p);
+ g_ptr_array_free(stream->files, TRUE);
g_ptr_array_free(stream->events_by_id, TRUE);
g_hash_table_destroy(stream->event_quark_to_id);
free_declaration_scope(stream->declaration_scope);
if (!strcmp(left, "major")) {
if (CTF_TRACE_FIELD_IS_SET(trace, major)) {
fprintf(fd, "[error] %s: major already declared in trace declaration\n", __func__);
- return -EPERM;
+ ret = -EPERM;
+ goto error;
}
ret = get_unary_unsigned(&node->u.ctf_expression.right, &trace->major);
if (ret) {
fprintf(fd, "[error] %s: unexpected unary expression for trace major number\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
CTF_TRACE_SET_FIELD(trace, major);
} else if (!strcmp(left, "minor")) {
if (CTF_TRACE_FIELD_IS_SET(trace, minor)) {
fprintf(fd, "[error] %s: minor already declared in trace declaration\n", __func__);
- return -EPERM;
+ ret = -EPERM;
+ goto error;
}
ret = get_unary_unsigned(&node->u.ctf_expression.right, &trace->minor);
if (ret) {
fprintf(fd, "[error] %s: unexpected unary expression for trace minor number\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
CTF_TRACE_SET_FIELD(trace, minor);
} else if (!strcmp(left, "uuid")) {
if (CTF_TRACE_FIELD_IS_SET(trace, uuid)) {
fprintf(fd, "[error] %s: uuid already declared in trace declaration\n", __func__);
- return -EPERM;
+ ret = -EPERM;
+ goto error;
}
ret = get_unary_uuid(&node->u.ctf_expression.right, &trace->uuid);
if (ret) {
fprintf(fd, "[error] %s: unexpected unary expression for trace uuid\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
CTF_TRACE_SET_FIELD(trace, uuid);
+ } else if (!strcmp(left, "byte_order")) {
+ struct ctf_node *right;
+ int byte_order;
+
+ if (CTF_TRACE_FIELD_IS_SET(trace, byte_order)) {
+ fprintf(fd, "[error] %s: endianness already declared in trace declaration\n", __func__);
+ ret = -EPERM;
+ goto error;
+ }
+ right = _cds_list_first_entry(&node->u.ctf_expression.right, struct ctf_node, siblings);
+ byte_order = get_trace_byte_order(fd, depth, right);
+ if (byte_order < 0)
+ return -EINVAL;
+ trace->byte_order = byte_order;
+ CTF_TRACE_SET_FIELD(trace, byte_order);
+ } else if (!strcmp(left, "packet.header")) {
+ struct declaration *declaration;
+
+ if (trace->packet_header_decl) {
+ fprintf(fd, "[error] %s: packet.header already declared in trace declaration\n", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+ declaration = ctf_type_specifier_list_visit(fd, depth,
+ _cds_list_first_entry(&node->u.ctf_expression.right,
+ struct ctf_node, siblings),
+ trace->declaration_scope, trace);
+ if (!declaration) {
+ ret = -EPERM;
+ goto error;
+ }
+ if (declaration->id != CTF_TYPE_STRUCT) {
+ ret = -EPERM;
+ goto error;
+ }
+ trace->packet_header_decl = container_of(declaration, struct declaration_struct, p);
}
+error:
g_free(left);
break;
}
/* TODO: declaration specifier should be added. */
}
- return 0;
+ return ret;
}
static
int ctf_trace_visit(FILE *fd, int depth, struct ctf_node *node, struct ctf_trace *trace)
{
+ struct definition_scope *parent_def_scope;
int ret = 0;
struct ctf_node *iter;
fprintf(fd, "[error] %s: missing uuid field in trace declaration\n", __func__);
goto error;
}
+
+ parent_def_scope = NULL;
+ if (trace->packet_header_decl) {
+ trace->packet_header =
+ container_of(
+ trace->packet_header_decl->p.definition_new(&trace->packet_header_decl->p,
+ parent_def_scope, 0, 0),
+ struct definition_struct, p);
+ set_dynamic_definition_scope(&trace->packet_header->p,
+ trace->packet_header->scope,
+ "trace.packet.header");
+ parent_def_scope = trace->packet_header->scope;
+ declaration_unref(&trace->packet_header_decl->p);
+ }
+ trace->definition_scope = parent_def_scope;
+
+ if (!CTF_TRACE_FIELD_IS_SET(trace, byte_order)) {
+ /* check that the packet header contains a "magic" field */
+ if (!trace->packet_header
+ || struct_declaration_lookup_field_index(trace->packet_header_decl, g_quark_from_static_string("magic")) < 0) {
+ ret = -EPERM;
+ fprintf(fd, "[error] %s: missing both byte_order and packet header magic number in trace declaration\n", __func__);
+ goto error_free_def;
+ }
+ }
return 0;
+error_free_def:
+ definition_unref(&trace->packet_header->p);
error:
g_ptr_array_free(trace->streams, TRUE);
free_declaration_scope(trace->declaration_scope);
struct stream_pos destp;
align_pos(srcp, float_declaration->p.alignment);
- init_pos(&destp, (char *) u.bits);
+ init_pos(&destp, -1);
+ destp.base = (char *) u.bits;
_ctf_float_copy(&destp, dest_declaration, srcp, float_declaration);
declaration_unref(&dest_declaration->p);
return u.v;
u.v = v;
align_pos(destp, float_declaration->p.alignment);
- init_pos(&srcp, (char *) u.bits);
+ init_pos(&srcp, -1);
+ srcp.base = (char *) u.bits;
_ctf_float_copy(destp, float_declaration, &srcp, src_declaration);
declaration_unref(&src_declaration->p);
}
struct stream_pos destp;
align_pos(srcp, float_declaration->p.alignment);
- init_pos(&destp, (char *) u.bits);
+ init_pos(&destp, -1);
+ destp.base = (char *) u.bits;
_ctf_float_copy(&destp, dest_declaration, srcp, float_declaration);
declaration_unref(&dest_declaration->p);
return u.v;
u.v = v;
align_pos(destp, float_declaration->p.alignment);
- init_pos(&srcp, (char *) u.bits);
+ init_pos(&srcp, -1);
+ srcp.base = (char *) u.bits;
_ctf_float_copy(destp, float_declaration, &srcp, src_declaration);
declaration_unref(&src_declaration->p);
}
#include <assert.h>
#include <glib.h>
+#define CTF_MAGIC 0xC1FC1FC1
+
struct ctf_trace;
struct ctf_stream;
struct ctf_event;
-struct ctf_stream_file {
- /* Information about stream backing file */
- int fd;
- char *mmap; /* current stream mmap */
+struct ctf_file_stream {
+ uint64_t stream_id;
+ struct ctf_stream *stream;
struct stream_pos pos; /* current stream position */
};
struct declaration_scope *root_declaration_scope;
struct declaration_scope *declaration_scope;
- GPtrArray *streams; /* Array of struct ctf_stream pointers*/
- struct ctf_stream_file metadata;
+ /* innermost definition scope. to be used as parent of stream. */
+ struct definition_scope *definition_scope;
+ GPtrArray *streams; /* Array of struct ctf_stream pointers */
+ struct ctf_file_stream metadata;
+
+ /* Declarations only used when parsing */
+ struct declaration_struct *packet_header_decl;
+
+ /* Definitions used afterward */
+ struct definition_struct *packet_header;
uint64_t major;
uint64_t minor;
uuid_t uuid;
- int byte_order;
+ int byte_order; /* trace BYTE_ORDER. 0 if unset. */
enum { /* Fields populated mask */
- CTF_TRACE_major = (1U << 0),
- CTF_TRACE_minor = (1U << 1),
- CTF_TRACE_uuid = (1U << 2),
+ CTF_TRACE_major = (1U << 0),
+ CTF_TRACE_minor = (1U << 1),
+ CTF_TRACE_uuid = (1U << 2),
+ CTF_TRACE_byte_order = (1U << 3),
+ CTF_TRACE_packet_header = (1U << 4),
} field_mask;
/* Information about trace backing directory and files */
CTF_STREAM_stream_id = (1 << 0),
} field_mask;
- struct ctf_stream_file file; /* Backing file */
+ GPtrArray *files; /* Array of struct ctf_file_stream pointers */
};
#define CTF_EVENT_SET_FIELD(ctf_event, field) \
#include <babeltrace/types.h>
#include <stdint.h>
+#include <unistd.h>
#include <glib.h>
-/*
- * Always update stream_pos with move_pos and init_pos.
- */
-struct stream_pos {
- char *base; /* Base address */
- size_t offset; /* Offset from base, in bits */
- int dummy; /* Dummy position, for length calculation */
+struct packet_index {
+ off_t offset; /* offset of the packet in the file, in bytes */
+ size_t packet_size; /* packet size, in bits */
+ size_t content_size; /* content size, in bits */
};
-static inline
-void init_pos(struct stream_pos *pos, char *base)
-{
- pos->base = base; /* initial base, page-aligned */
- pos->offset = 0;
- pos->dummy = false;
-}
-
-/*
- * move_pos - move position of a relative bit offset
- *
- * TODO: allow larger files by updating base too.
- */
-static inline
-void move_pos(struct stream_pos *pos, size_t offset)
-{
- pos->offset = pos->offset + offset;
-}
-
/*
- * align_pos - align position on a bit offset (> 0)
- *
- * TODO: allow larger files by updating base too.
+ * Always update stream_pos with move_pos and init_pos.
*/
-static inline
-void align_pos(struct stream_pos *pos, size_t offset)
-{
- pos->offset += offset_align(pos->offset, offset);
-}
+struct stream_pos {
+ int fd; /* backing file fd. -1 if unset. */
+ GArray *packet_index; /* contains struct packet_index */
-static inline
-void copy_pos(struct stream_pos *dest, struct stream_pos *src)
-{
- memcpy(dest, src, sizeof(struct stream_pos));
-}
+ /* Current position */
+ off_t mmap_offset; /* mmap offset in the file, in bytes */
+ size_t packet_size; /* current packet size, in bits */
+ size_t content_size; /* current content size, in bits */
+ char *base; /* mmap base address */
+ size_t offset; /* offset from base, in bits */
-static inline
-char *get_pos_addr(struct stream_pos *pos)
-{
- /* Only makes sense to get the address after aligning on CHAR_BIT */
- assert(!(pos->offset % CHAR_BIT));
- return pos->base + (pos->offset / CHAR_BIT);
-}
+ int dummy; /* dummy position, for length calculation */
+};
/*
* IMPORTANT: All lengths (len) and offsets (start, end) are expressed in bits,
void ctf_sequence_end(struct stream_pos *pos,
const struct declaration_sequence *sequence_declaration);
+void move_pos_slow(struct stream_pos *pos, size_t offset);
+
+static inline
+void init_pos(struct stream_pos *pos, int fd)
+{
+ pos->fd = fd;
+ pos->mmap_offset = 0;
+ pos->packet_size = 0;
+ pos->content_size = 0;
+ pos->base = NULL;
+ pos->offset = 0;
+ pos->dummy = false;
+}
+
+/*
+ * move_pos - move position of a relative bit offset
+ *
+ * TODO: allow larger files by updating base too.
+ */
+static inline
+void move_pos(struct stream_pos *pos, size_t offset)
+{
+ if (pos->fd >= 0 && pos->offset + offset >= pos->content_size)
+ move_pos_slow(pos, offset);
+ else
+ pos->offset += offset;
+}
+
+/*
+ * align_pos - align position on a bit offset (> 0)
+ *
+ * TODO: allow larger files by updating base too.
+ */
+static inline
+void align_pos(struct stream_pos *pos, size_t offset)
+{
+ pos->offset += offset_align(pos->offset, offset);
+}
+
+static inline
+void copy_pos(struct stream_pos *dest, struct stream_pos *src)
+{
+ memcpy(dest, src, sizeof(struct stream_pos));
+}
+
+static inline
+char *get_pos_addr(struct stream_pos *pos)
+{
+ /* Only makes sense to get the address after aligning on CHAR_BIT */
+ assert(!(pos->offset % CHAR_BIT));
+ return pos->base + (pos->offset / CHAR_BIT);
+}
+
#endif /* _BABELTRACE_CTF_TYPES_H */
struct definition p;
struct declaration_array *declaration;
struct definition_scope *scope;
- struct field current_element; /* struct field */
+ GArray *elems; /* struct field */
};
struct declaration_sequence {
struct declaration_sequence *declaration;
struct definition_scope *scope;
struct definition_integer *len;
- struct field current_element; /* struct field */
+ GArray *elems; /* struct field */
};
int register_declaration(GQuark declaration_name,
/*
* Returns the index of a field within a structure.
*/
-unsigned long struct_declaration_lookup_field_index(struct declaration_struct *struct_declaration,
+int struct_declaration_lookup_field_index(struct declaration_struct *struct_declaration,
GQuark field_name);
/*
* field returned only valid as long as the field structure is not appended to.
*/
struct declaration_field *
struct_declaration_get_field_from_index(struct declaration_struct *struct_declaration,
- unsigned long index);
+ int index);
struct field *
-struct_get_field_from_index(struct definition_struct *struct_definition,
- unsigned long index);
+struct_definition_get_field_from_index(struct definition_struct *struct_definition,
+ int index);
/*
* The tag enumeration is validated to ensure that it contains only mappings
struct declaration_array *
array_declaration_new(size_t len, struct declaration *elem_declaration,
struct declaration_scope *parent_scope);
+struct definition *array_index(struct definition_array *array, uint64_t i);
/*
* int_declaration and elem_declaration passed as parameter now belong
sequence_declaration_new(struct declaration_integer *len_declaration,
struct declaration *elem_declaration,
struct declaration_scope *parent_scope);
+struct definition *sequence_index(struct definition_sequence *sequence, uint64_t i);
/*
* in: path (dot separated), out: q (GArray of GQuark)
#include <babeltrace/compiler.h>
#include <babeltrace/format.h>
+#include <inttypes.h>
static
struct definition *_array_definition_new(struct declaration *declaration,
uint64_t i;
fsrc->array_begin(src, array_declaration);
- fdest->array_begin(dest, array_declaration);
+ if (fdest)
+ fdest->array_begin(dest, array_declaration);
for (i = 0; i < array_declaration->len; i++) {
- struct definition *elem = array->current_element.definition;
+ struct definition *elem =
+ g_array_index(array->elems, struct field, i).definition;
elem->declaration->copy(dest, fdest, src, fsrc, elem);
}
fsrc->array_end(src, array_declaration);
- fdest->array_end(dest, array_declaration);
+ if (fdest)
+ fdest->array_end(dest, array_declaration);
}
static
struct declaration_array *array_declaration =
container_of(declaration, struct declaration_array, p);
struct definition_array *array;
+ uint64_t i;
array = g_new(struct definition_array, 1);
declaration_ref(&array_declaration->p);
array->p.ref = 1;
array->p.index = index;
array->scope = new_definition_scope(parent_scope, field_name);
- array->current_element.definition =
- array_declaration->elem->definition_new(array_declaration->elem,
- parent_scope,
- g_quark_from_static_string("[]"),
- 0);
+ array->elems = g_array_sized_new(FALSE, TRUE, sizeof(struct field),
+ array_declaration->len);
+ g_array_set_size(array->elems, array_declaration->len);
+ for (i = 0; i < array_declaration->len; i++) {
+ struct field *field;
+ GString *str;
+ GQuark name;
+
+ str = g_string_new("");
+ g_string_printf(str, "[%" PRIu64 "]", i);
+ name = g_quark_from_string(str->str);
+ (void) g_string_free(str, TRUE);
+
+ field = &g_array_index(array->elems, struct field, i);
+ field->name = name;
+ field->definition = array_declaration->elem->definition_new(array_declaration->elem,
+ array->scope,
+ name, i);
+ }
return &array->p;
}
{
struct definition_array *array =
container_of(definition, struct definition_array, p);
- struct definition *elem_definition =
- array->current_element.definition;
+ uint64_t i;
- elem_definition->declaration->definition_free(elem_definition);
+ for (i = 0; i < array->elems->len; i++) {
+ struct field *field;
+
+ field = &g_array_index(array->elems, struct field, i);
+ field->definition->declaration->definition_free(field->definition);
+ }
+ (void) g_array_free(array->elems, TRUE);
free_definition_scope(array->scope);
declaration_unref(array->p.declaration);
g_free(array);
}
+
+struct definition *array_index(struct definition_array *array, uint64_t i)
+{
+ if (i >= array->elems->len)
+ return NULL;
+ return g_array_index(array->elems, struct field, i).definition;
+}
* now to test enum read and write code.
*/
v = g_array_index(array, GQuark, 0);
- return fdest->enum_write(dest, enum_declaration, v);
+ if (fdest)
+ fdest->enum_write(dest, enum_declaration, v);
}
static
double v;
v = fsrc->double_read(srcp, float_declaration);
- fdest->double_write(destp, float_declaration, v);
+ if (fdest)
+ fdest->double_write(destp, float_declaration, v);
}
}
uint64_t v;
v = fsrc->uint_read(src, integer_declaration);
- fdest->uint_write(dest, integer_declaration, v);
+ if (fdest)
+ fdest->uint_write(dest, integer_declaration, v);
} else {
int64_t v;
v = fsrc->int_read(src, integer_declaration);
- fdest->int_write(dest, integer_declaration, v);
+ if (fdest)
+ fdest->int_write(dest, integer_declaration, v);
}
}
#include <babeltrace/compiler.h>
#include <babeltrace/format.h>
+#include <inttypes.h>
#ifndef max
#define max(a, b) ((a) < (b) ? (b) : (a))
struct definition_sequence *sequence =
container_of(definition, struct definition_sequence, p);
struct declaration_sequence *sequence_declaration = sequence->declaration;
- uint64_t i;
+ uint64_t len, oldlen, i;
fsrc->sequence_begin(src, sequence_declaration);
- fdest->sequence_begin(dest, sequence_declaration);
+ if (fdest)
+ fdest->sequence_begin(dest, sequence_declaration);
sequence->len->p.declaration->copy(dest, fdest, src, fsrc,
&sequence->len->p);
+ len = sequence->len->value._unsigned;
+ g_array_set_size(sequence->elems, len);
+ /*
+ * Yes, large sequences could be _painfully slow_ to parse due
+ * to memory allocation for each event read. At least, never
+ * shrink the sequence. Note: the sequence GArray len should
+ * never be used as indicator of the current sequence length.
+ * One should always look at the sequence->len->value._unsigned
+ * value for that.
+ */
+ oldlen = sequence->elems->len;
+ if (oldlen < len)
+ g_array_set_size(sequence->elems, len);
+
+ for (i = oldlen; i < len; i++) {
+ struct field *field;
+ GString *str;
+ GQuark name;
+
+ str = g_string_new("");
+ g_string_printf(str, "[%" PRIu64 "]", i);
+ (void) g_string_free(str, TRUE);
+ name = g_quark_from_string(str->str);
- for (i = 0; i < sequence->len->value._unsigned; i++) {
- struct definition *elem =
- sequence->current_element.definition;
- elem->declaration->copy(dest, fdest, src, fsrc, elem);
+ field = &g_array_index(sequence->elems, struct field, i);
+ field->name = name;
+ field->definition = sequence_declaration->elem->definition_new(sequence_declaration->elem,
+ sequence->scope,
+ name, i);
+ field->definition->declaration->copy(dest, fdest, src, fsrc, field->definition);
}
fsrc->sequence_end(src, sequence_declaration);
- fdest->sequence_end(dest, sequence_declaration);
+ if (fdest)
+ fdest->sequence_end(dest, sequence_declaration);
}
static
sequence->p.index = index;
sequence->scope = new_definition_scope(parent_scope, field_name);
len_parent = sequence_declaration->len_declaration->p.definition_new(&sequence_declaration->len_declaration->p,
- parent_scope,
+ sequence->scope,
g_quark_from_static_string("length"), 0);
sequence->len =
container_of(len_parent, struct definition_integer, p);
- sequence->current_element.definition =
- sequence_declaration->elem->definition_new(sequence_declaration->elem,
- parent_scope,
- g_quark_from_static_string("[]"), 1);
+ sequence->elems = g_array_new(FALSE, TRUE, sizeof(struct field));
return &sequence->p;
}
struct definition_sequence *sequence =
container_of(definition, struct definition_sequence, p);
struct definition *len_definition = &sequence->len->p;
- struct definition *elem_definition =
- sequence->current_element.definition;
+ uint64_t i;
+
+ for (i = 0; i < sequence->elems->len; i++) {
+ struct field *field;
+ field = &g_array_index(sequence->elems, struct field, i);
+ field->definition->declaration->definition_free(field->definition);
+ }
+ (void) g_array_free(sequence->elems, TRUE);
len_definition->declaration->definition_free(len_definition);
- elem_definition->declaration->definition_free(elem_definition);
free_definition_scope(sequence->scope);
declaration_unref(sequence->p.declaration);
g_free(sequence);
}
+
+struct definition *sequence_index(struct definition_sequence *sequence, uint64_t i)
+{
+ if (i >= sequence->len->value._unsigned)
+ return NULL;
+ assert(i < sequence->elems->len);
+ return g_array_index(sequence->elems, struct field, i).definition;
+}
container_of(definition, struct definition_string, p);
struct declaration_string *string_declaration = string->declaration;
- if (fsrc->string_copy == fdest->string_copy) {
+ if (fdest && (fsrc->string_copy == fdest->string_copy)) {
fsrc->string_copy(dest, src, string_declaration);
} else {
char *tmp = NULL;
fsrc->string_read(&tmp, src, string_declaration);
- fdest->string_write(dest, tmp, string_declaration);
+ if (fdest)
+ fdest->string_write(dest, tmp, string_declaration);
fsrc->string_free_temp(tmp);
}
}
unsigned long i;
fsrc->struct_begin(src, struct_declaration);
- fdest->struct_begin(dest, struct_declaration);
+ if (fdest)
+ fdest->struct_begin(dest, struct_declaration);
for (i = 0; i < _struct->fields->len; i++) {
struct field *field = &g_array_index(_struct->fields,
}
fsrc->struct_end(src, struct_declaration);
- fdest->struct_end(dest, struct_declaration);
+ if (fdest)
+ fdest->struct_end(dest, struct_declaration);
}
static
field_declaration->alignment);
}
-unsigned long
- struct_declaration_lookup_field_index(struct declaration_struct *struct_declaration,
+/*
+ * struct_declaration_lookup_field_index - returns field index
+ *
+ * Returns the index of a field in a structure, or -1 if it does not
+ * exist.
+ */
+int struct_declaration_lookup_field_index(struct declaration_struct *struct_declaration,
GQuark field_name)
{
- unsigned long index;
-
- index = (unsigned long) g_hash_table_lookup(struct_declaration->fields_by_name,
- (gconstpointer) (unsigned long) field_name);
- return index;
+ gpointer index;
+ gboolean found;
+
+ found = g_hash_table_lookup_extended(struct_declaration->fields_by_name,
+ (gconstpointer) (unsigned long) field_name,
+ NULL, &index);
+ if (!found)
+ return -1;
+ return (int) (unsigned long) index;
}
/*
*/
struct declaration_field *
struct_declaration_get_field_from_index(struct declaration_struct *struct_declaration,
- unsigned long index)
+ int index)
{
+ if (index < 0)
+ return NULL;
return &g_array_index(struct_declaration->fields, struct declaration_field, index);
}
*/
struct field *
struct_definition_get_field_from_index(struct definition_struct *_struct,
- unsigned long index)
+ int index)
{
+ if (index < 0)
+ return NULL;
return &g_array_index(_struct->fields, struct field, index);
}
struct declaration *field_declaration;
fsrc->variant_begin(src, variant_declaration);
- fdest->variant_begin(dest, variant_declaration);
+ if (fdest)
+ fdest->variant_begin(dest, variant_declaration);
field = variant_get_current_field(variant);
field_declaration = field->definition->declaration;
field_declaration->copy(dest, fdest, src, fsrc, field->definition);
fsrc->variant_end(src, variant_declaration);
- fdest->variant_end(dest, variant_declaration);
+ if (fdest)
+ fdest->variant_end(dest, variant_declaration);
}
static