From 0f980a3595f61930659e392b1248c59490dd5a22 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Thu, 5 May 2011 00:21:35 -0400 Subject: [PATCH] Start packet mmap work Signed-off-by: Mathieu Desnoyers --- converter/babeltrace.c | 8 +- formats/ctf/ctf.c | 284 +++++++++++++++++- .../ctf-test/succeed/ctf-embedded-1.txt | 2 +- .../metadata/ctf-visitor-generate-io-struct.c | 249 ++++++++++++--- formats/ctf/types/float.c | 12 +- include/babeltrace/ctf/metadata.h | 33 +- include/babeltrace/ctf/types.h | 116 ++++--- include/babeltrace/types.h | 14 +- types/array.c | 53 +++- types/enum.c | 3 +- types/float.c | 3 +- types/integer.c | 6 +- types/sequence.c | 68 ++++- types/string.c | 5 +- types/struct.c | 37 ++- types/variant.c | 6 +- 16 files changed, 729 insertions(+), 170 deletions(-) diff --git a/converter/babeltrace.c b/converter/babeltrace.c index b603dee5..dd6b9a2c 100644 --- a/converter/babeltrace.c +++ b/converter/babeltrace.c @@ -69,7 +69,8 @@ static void list_formats(FILE *fp) 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"); @@ -96,6 +97,11 @@ static int parse_options(int argc, char **argv) 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); diff --git a/formats/ctf/ctf.c b/formats/ctf/ctf.c index 9b2f0fbf..0c635d57 100644 --- a/formats/ctf/ctf.c +++ b/formats/ctf/ctf.c @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -29,11 +32,17 @@ #include #include - #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 { @@ -69,6 +78,35 @@ static struct format ctf_format = { .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). @@ -80,17 +118,17 @@ int ctf_open_trace_metadata_read(struct trace_descriptor *td) 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; @@ -133,7 +171,211 @@ end: 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; } @@ -161,6 +403,9 @@ int ctf_open_trace_read(struct trace_descriptor *td, const char *path, int flags ret = -ENOENT; goto error_dirfd; } + + td->ctf_trace.streams = g_ptr_array_new(); + /* * Keep the metadata file separate. */ @@ -186,7 +431,6 @@ int ctf_open_trace_read(struct trace_descriptor *td, const char *path, int flags if (ret) { fprintf(stdout, "Readdir error.\n"); goto readdir_error; - } if (!diriter) break; @@ -194,6 +438,7 @@ int ctf_open_trace_read(struct trace_descriptor *td, const char *path, int flags || !strcmp(diriter->d_name, "..") || !strcmp(diriter->d_name, "metadata")) continue; + /* TODO: open file stream */ } free(dirent); @@ -202,6 +447,7 @@ int ctf_open_trace_read(struct trace_descriptor *td, const char *path, int flags 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); @@ -262,8 +508,32 @@ error: 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); } diff --git a/formats/ctf/metadata/ctf-test/succeed/ctf-embedded-1.txt b/formats/ctf/metadata/ctf-test/succeed/ctf-embedded-1.txt index 05b1c98a..4a97cdf7 100644 --- a/formats/ctf/metadata/ctf-test/succeed/ctf-embedded-1.txt +++ b/formats/ctf/metadata/ctf-test/succeed/ctf-embedded-1.txt @@ -2,7 +2,7 @@ trace { 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 */ diff --git a/formats/ctf/metadata/ctf-visitor-generate-io-struct.c b/formats/ctf/metadata/ctf-visitor-generate-io-struct.c index f4776a0a..1981832e 100644 --- a/formats/ctf/metadata/ctf-visitor-generate-io-struct.c +++ b/formats/ctf/metadata/ctf-visitor-generate-io-struct.c @@ -993,6 +993,28 @@ int get_boolean(FILE *fd, int depth, struct ctf_node *unary_expression) } +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) @@ -1300,12 +1322,14 @@ int ctf_event_declaration_visit(FILE *fd, int depth, struct ctf_node *node, stru 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); @@ -1313,28 +1337,33 @@ int ctf_event_declaration_visit(FILE *fd, int depth, struct ctf_node *node, stru } 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")) { @@ -1342,34 +1371,45 @@ int ctf_event_declaration_visit(FILE *fd, int depth, struct ctf_node *node, stru 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; } @@ -1378,7 +1418,7 @@ int ctf_event_declaration_visit(FILE *fd, int depth, struct ctf_node *node, stru /* TODO: declaration specifier should be added. */ } - return 0; + return ret; } static @@ -1402,15 +1442,23 @@ int ctf_event_visit(FILE *fd, int depth, struct ctf_node *node, 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); @@ -1485,12 +1533,14 @@ int ctf_stream_declaration_visit(FILE *fd, int depth, struct ctf_node *node, str 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")) { @@ -1498,50 +1548,66 @@ int ctf_stream_declaration_visit(FILE *fd, int depth, struct ctf_node *node, str 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; } @@ -1550,7 +1616,7 @@ int ctf_stream_declaration_visit(FILE *fd, int depth, struct ctf_node *node, str /* TODO: declaration specifier should be added. */ } - return 0; + return ret; } static @@ -1566,12 +1632,25 @@ int ctf_stream_visit(FILE *fd, int depth, struct ctf_node *node, 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; @@ -1580,7 +1659,7 @@ int ctf_stream_visit(FILE *fd, int depth, struct ctf_node *node, 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( @@ -1625,6 +1704,7 @@ error: 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); @@ -1663,37 +1743,80 @@ int ctf_trace_declaration_visit(FILE *fd, int depth, struct ctf_node *node, stru 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; } @@ -1702,12 +1825,13 @@ int ctf_trace_declaration_visit(FILE *fd, int depth, struct ctf_node *node, stru /* 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; @@ -1735,8 +1859,35 @@ int ctf_trace_visit(FILE *fd, int depth, struct ctf_node *node, struct ctf_trace 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); diff --git a/formats/ctf/types/float.c b/formats/ctf/types/float.c index 72c77912..3d84952b 100644 --- a/formats/ctf/types/float.c +++ b/formats/ctf/types/float.c @@ -119,7 +119,8 @@ double ctf_double_read(struct stream_pos *srcp, 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; @@ -139,7 +140,8 @@ void ctf_double_write(struct stream_pos *destp, 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); } @@ -156,7 +158,8 @@ long double ctf_ldouble_read(struct stream_pos *srcp, 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; @@ -176,7 +179,8 @@ void ctf_ldouble_write(struct stream_pos *destp, 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); } diff --git a/include/babeltrace/ctf/metadata.h b/include/babeltrace/ctf/metadata.h index d833a56d..330238ba 100644 --- a/include/babeltrace/ctf/metadata.h +++ b/include/babeltrace/ctf/metadata.h @@ -27,14 +27,15 @@ #include #include +#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 */ }; @@ -58,18 +59,28 @@ struct ctf_trace { 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 */ @@ -117,7 +128,7 @@ struct ctf_stream { 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) \ diff --git a/include/babeltrace/ctf/types.h b/include/babeltrace/ctf/types.h index c311b532..91c3303c 100644 --- a/include/babeltrace/ctf/types.h +++ b/include/babeltrace/ctf/types.h @@ -21,60 +21,31 @@ #include #include +#include #include -/* - * 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, @@ -140,4 +111,57 @@ void ctf_sequence_begin(struct stream_pos *pos, 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 */ diff --git a/include/babeltrace/types.h b/include/babeltrace/types.h index 3d2b70a4..d01b6724 100644 --- a/include/babeltrace/types.h +++ b/include/babeltrace/types.h @@ -275,7 +275,7 @@ struct definition_array { struct definition p; struct declaration_array *declaration; struct definition_scope *scope; - struct field current_element; /* struct field */ + GArray *elems; /* struct field */ }; struct declaration_sequence { @@ -290,7 +290,7 @@ struct definition_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, @@ -409,17 +409,17 @@ void struct_declaration_add_field(struct declaration_struct *struct_declaration, /* * 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 @@ -457,6 +457,7 @@ struct field *variant_get_current_field(struct definition_variant *variant); 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 @@ -466,6 +467,7 @@ struct declaration_sequence * 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) diff --git a/types/array.c b/types/array.c index c218a11a..d8ddd637 100644 --- a/types/array.c +++ b/types/array.c @@ -18,6 +18,7 @@ #include #include +#include static struct definition *_array_definition_new(struct declaration *declaration, @@ -36,14 +37,17 @@ void array_copy(struct stream_pos *dest, const struct format *fdest, 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 @@ -91,6 +95,7 @@ struct definition * 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); @@ -99,11 +104,25 @@ struct definition * 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; } @@ -112,11 +131,23 @@ void _array_definition_free(struct definition *definition) { 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; +} diff --git a/types/enum.c b/types/enum.c index 4c2d2b77..368c21a6 100644 --- a/types/enum.c +++ b/types/enum.c @@ -371,7 +371,8 @@ void enum_copy(struct stream_pos *dest, const struct format *fdest, * 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 diff --git a/types/float.c b/types/float.c index 576f8622..3b2523e2 100644 --- a/types/float.c +++ b/types/float.c @@ -42,7 +42,8 @@ void float_copy(struct stream_pos *destp, 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); } } diff --git a/types/integer.c b/types/integer.c index 7b7d22c8..16cd903d 100644 --- a/types/integer.c +++ b/types/integer.c @@ -40,12 +40,14 @@ void integer_copy(struct stream_pos *dest, const struct format *fdest, 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); } } diff --git a/types/sequence.c b/types/sequence.c index a719ee99..50763d7a 100644 --- a/types/sequence.c +++ b/types/sequence.c @@ -18,6 +18,7 @@ #include #include +#include #ifndef max #define max(a, b) ((a) < (b) ? (b) : (a)) @@ -37,21 +38,48 @@ void sequence_copy(struct stream_pos *dest, const struct format *fdest, 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 @@ -110,14 +138,11 @@ struct definition *_sequence_definition_new(struct declaration *declaration, 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; } @@ -127,12 +152,25 @@ void _sequence_definition_free(struct definition *definition) 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; +} diff --git a/types/string.c b/types/string.c index f8778c84..796f05cd 100644 --- a/types/string.c +++ b/types/string.c @@ -35,13 +35,14 @@ void string_copy(struct stream_pos *dest, const struct format *fdest, 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); } } diff --git a/types/struct.c b/types/struct.c index 69e3a553..ea2e28c1 100644 --- a/types/struct.c +++ b/types/struct.c @@ -40,7 +40,8 @@ void struct_copy(struct stream_pos *dest, const struct format *fdest, 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, @@ -51,7 +52,8 @@ void struct_copy(struct stream_pos *dest, const struct format *fdest, } fsrc->struct_end(src, struct_declaration); - fdest->struct_end(dest, struct_declaration); + if (fdest) + fdest->struct_end(dest, struct_declaration); } static @@ -184,15 +186,24 @@ void struct_declaration_add_field(struct declaration_struct *struct_declaration, 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; } /* @@ -200,8 +211,10 @@ unsigned long */ 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); } @@ -210,7 +223,9 @@ struct declaration_field * */ 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); } diff --git a/types/variant.c b/types/variant.c index a61d895c..1af0c790 100644 --- a/types/variant.c +++ b/types/variant.c @@ -38,14 +38,16 @@ void variant_copy(struct stream_pos *dest, const struct format *fdest, 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 -- 2.34.1