From bc37ae52aa6face901440bf7eb2171104b5343d8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 16 Oct 2014 17:45:35 -0400 Subject: [PATCH] Add Trace CTF IR type MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Refactored the Writer interface to expose a Trace object which will be exposed by the CTF reader. Signed-off-by: Jérémie Galarneau --- formats/ctf/ir/Makefile.am | 3 +- formats/ctf/ir/event.c | 2 +- formats/ctf/ir/stream.c | 6 +- formats/ctf/ir/trace.c | 639 ++++++++++++++++++ formats/ctf/writer/writer.c | 518 +------------- include/Makefile.am | 3 +- include/babeltrace/ctf-ir/clock-internal.h | 2 +- .../babeltrace/ctf-ir/event-types-internal.h | 2 +- include/babeltrace/ctf-ir/stream-internal.h | 1 + include/babeltrace/ctf-ir/trace.h | 145 ++++ .../babeltrace/ctf-writer/writer-internal.h | 44 +- 11 files changed, 826 insertions(+), 539 deletions(-) create mode 100644 formats/ctf/ir/trace.c create mode 100644 include/babeltrace/ctf-ir/trace.h diff --git a/formats/ctf/ir/Makefile.am b/formats/ctf/ir/Makefile.am index 295cc809..955efc74 100644 --- a/formats/ctf/ir/Makefile.am +++ b/formats/ctf/ir/Makefile.am @@ -8,7 +8,8 @@ libctf_ir_la_SOURCES = \ event-fields.c \ event-types.c \ stream.c \ - stream-class.c + stream-class.c \ + trace.c libctf_ir_la_LIBADD = \ $(top_builddir)/lib/libbabeltrace.la diff --git a/formats/ctf/ir/event.c b/formats/ctf/ir/event.c index caea354a..99294979 100644 --- a/formats/ctf/ir/event.c +++ b/formats/ctf/ir/event.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include static diff --git a/formats/ctf/ir/stream.c b/formats/ctf/ir/stream.c index d9bd5ef8..57b89379 100644 --- a/formats/ctf/ir/stream.c +++ b/formats/ctf/ir/stream.c @@ -326,7 +326,11 @@ int bt_ctf_stream_flush(struct bt_ctf_stream *stream) struct bt_ctf_field *integer = NULL; struct ctf_stream_pos packet_context_pos; - if (!stream) { + if (!stream || stream->pos.fd < 0) { + /* + * Stream does not have an associated fd. It is, + * therefore, not a stream being used to write events. + */ ret = -1; goto end; } diff --git a/formats/ctf/ir/trace.c b/formats/ctf/ir/trace.c new file mode 100644 index 00000000..073e4abf --- /dev/null +++ b/formats/ctf/ir/trace.c @@ -0,0 +1,639 @@ +/* + * trace.c + * + * Babeltrace CTF IR - Trace + * + * Copyright 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_IDENTIFIER_SIZE 128 +#define DEFAULT_METADATA_STRING_SIZE 4096 + +static +void environment_variable_destroy(struct environment_variable *var); +static +void bt_ctf_trace_destroy(struct bt_ctf_ref *ref); +static +int init_trace_packet_header(struct bt_ctf_trace *trace); + +static +const char * const reserved_keywords_str[] = {"align", "callsite", + "const", "char", "clock", "double", "enum", "env", "event", + "floating_point", "float", "integer", "int", "long", "short", "signed", + "stream", "string", "struct", "trace", "typealias", "typedef", + "unsigned", "variant", "void" "_Bool", "_Complex", "_Imaginary"}; + +static +const unsigned int field_type_aliases_alignments[] = { + [FIELD_TYPE_ALIAS_UINT5_T] = 1, + [FIELD_TYPE_ALIAS_UINT8_T ... FIELD_TYPE_ALIAS_UINT16_T] = 8, + [FIELD_TYPE_ALIAS_UINT27_T] = 1, + [FIELD_TYPE_ALIAS_UINT32_T ... FIELD_TYPE_ALIAS_UINT64_T] = 8, +}; + +static +const unsigned int field_type_aliases_sizes[] = { + [FIELD_TYPE_ALIAS_UINT5_T] = 5, + [FIELD_TYPE_ALIAS_UINT8_T] = 8, + [FIELD_TYPE_ALIAS_UINT16_T] = 16, + [FIELD_TYPE_ALIAS_UINT27_T] = 27, + [FIELD_TYPE_ALIAS_UINT32_T] = 32, + [FIELD_TYPE_ALIAS_UINT64_T] = 64, +}; + +static GHashTable *reserved_keywords_set; +static int init_done; +static int global_data_refcount; + +struct bt_ctf_trace *bt_ctf_trace_create(void) +{ + struct bt_ctf_trace *trace = NULL; + + trace = g_new0(struct bt_ctf_trace, 1); + if (!trace) { + goto error; + } + + bt_ctf_trace_set_byte_order(trace, BT_CTF_BYTE_ORDER_NATIVE); + bt_ctf_ref_init(&trace->ref_count); + trace->environment = g_ptr_array_new_with_free_func( + (GDestroyNotify)environment_variable_destroy); + trace->clocks = g_ptr_array_new_with_free_func( + (GDestroyNotify)bt_ctf_clock_put); + trace->streams = g_ptr_array_new_with_free_func( + (GDestroyNotify)bt_ctf_stream_put); + trace->stream_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify)bt_ctf_stream_class_put); + if (!trace->environment || !trace->clocks || + !trace->stream_classes || !trace->streams) { + goto error_destroy; + } + + /* Generate a trace UUID */ + uuid_generate(trace->uuid); + if (init_trace_packet_header(trace)) { + goto error_destroy; + } + + return trace; + +error_destroy: + bt_ctf_trace_destroy(&trace->ref_count); + trace = NULL; +error: + return trace; +} + +void bt_ctf_trace_destroy(struct bt_ctf_ref *ref) +{ + struct bt_ctf_trace *trace; + + if (!ref) { + return; + } + + trace = container_of(ref, struct bt_ctf_trace, ref_count); + if (trace->environment) { + g_ptr_array_free(trace->environment, TRUE); + } + + if (trace->clocks) { + g_ptr_array_free(trace->clocks, TRUE); + } + + if (trace->streams) { + g_ptr_array_free(trace->streams, TRUE); + } + + if (trace->stream_classes) { + g_ptr_array_free(trace->stream_classes, TRUE); + } + + bt_ctf_field_type_put(trace->trace_packet_header_type); + bt_ctf_field_put(trace->trace_packet_header); + g_free(trace); +} + +struct bt_ctf_stream *bt_ctf_trace_create_stream(struct bt_ctf_trace *trace, + struct bt_ctf_stream_class *stream_class) +{ + int ret; + int stream_class_found = 0; + size_t i; + struct bt_ctf_stream *stream = NULL; + + if (!trace || !stream_class) { + goto error; + } + + ret = bt_ctf_stream_class_set_byte_order(stream_class, + trace->byte_order == LITTLE_ENDIAN ? + BT_CTF_BYTE_ORDER_LITTLE_ENDIAN : BT_CTF_BYTE_ORDER_BIG_ENDIAN); + if (ret) { + goto error; + } + + stream = bt_ctf_stream_create(stream_class); + if (!stream) { + goto error; + } + + for (i = 0; i < trace->stream_classes->len; i++) { + if (trace->stream_classes->pdata[i] == stream_class) { + stream_class_found = 1; + } + } + + if (!stream_class_found) { + int64_t stream_id = bt_ctf_stream_class_get_id(stream_class); + + if (stream_id < 0) { + /* Try to assign a new stream id */ + if (bt_ctf_stream_class_set_id(stream->stream_class, + trace->next_stream_id++)) { + goto error; + } + } + + for (i = 0; i < trace->stream_classes->len; i++) { + if (stream_id == bt_ctf_stream_class_get_id( + trace->stream_classes->pdata[i])) { + /* Duplicate stream id found */ + goto error; + } + } + bt_ctf_stream_class_get(stream->stream_class); + g_ptr_array_add(trace->stream_classes, stream->stream_class); + } + + bt_ctf_stream_get(stream); + g_ptr_array_add(trace->streams, stream); + trace->frozen = 1; + return stream; + +error: + bt_ctf_stream_put(stream); + return NULL; +} + +int bt_ctf_trace_add_environment_field(struct bt_ctf_trace *trace, + const char *name, + const char *value) +{ + struct environment_variable *var = NULL; + char *escaped_value = NULL; + int ret = 0; + + if (!trace || !name || !value || validate_identifier(name)) { + ret = -1; + goto error; + } + + if (strchr(name, ' ')) { + ret = -1; + goto error; + } + + var = g_new0(struct environment_variable, 1); + if (!var) { + ret = -1; + goto error; + } + + escaped_value = g_strescape(value, NULL); + if (!escaped_value) { + ret = -1; + goto error; + } + + var->name = g_string_new(name); + var->value = g_string_new(escaped_value); + g_free(escaped_value); + if (!var->name || !var->value) { + ret = -1; + goto error; + } + + g_ptr_array_add(trace->environment, var); + return ret; + +error: + if (var && var->name) { + g_string_free(var->name, TRUE); + } + + if (var && var->value) { + g_string_free(var->value, TRUE); + } + + g_free(var); + return ret; +} + +int bt_ctf_trace_add_clock(struct bt_ctf_trace *trace, + struct bt_ctf_clock *clock) +{ + int ret = 0; + struct search_query query = { .value = clock, .found = 0 }; + + if (!trace || !clock) { + ret = -1; + goto end; + } + + /* Check for duplicate clocks */ + g_ptr_array_foreach(trace->clocks, value_exists, &query); + if (query.found) { + ret = -1; + goto end; + } + + bt_ctf_clock_get(clock); + g_ptr_array_add(trace->clocks, clock); +end: + return ret; +} + +BT_HIDDEN +const char *get_byte_order_string(int byte_order) +{ + const char *string; + + switch (byte_order) { + case LITTLE_ENDIAN: + string = "le"; + break; + case BIG_ENDIAN: + string = "be"; + break; + default: + string = "unknown"; + break; + } + + return string; +} + +static +int append_trace_metadata(struct bt_ctf_trace *trace, + struct metadata_context *context) +{ + unsigned char *uuid = trace->uuid; + int ret; + + g_string_append(context->string, "trace {\n"); + + g_string_append(context->string, "\tmajor = 1;\n"); + g_string_append(context->string, "\tminor = 8;\n"); + + g_string_append_printf(context->string, + "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + g_string_append_printf(context->string, "\tbyte_order = %s;\n", + get_byte_order_string(trace->byte_order)); + + g_string_append(context->string, "\tpacket.header := "); + context->current_indentation_level++; + g_string_assign(context->field_name, ""); + ret = bt_ctf_field_type_serialize(trace->trace_packet_header_type, + context); + if (ret) { + goto end; + } + context->current_indentation_level--; + + g_string_append(context->string, ";\n};\n\n"); +end: + return ret; +} + +static +void append_env_field_metadata(struct environment_variable *var, + struct metadata_context *context) +{ + g_string_append_printf(context->string, "\t%s = \"%s\";\n", + var->name->str, var->value->str); +} + +static +void append_env_metadata(struct bt_ctf_trace *trace, + struct metadata_context *context) +{ + if (trace->environment->len == 0) { + return; + } + + g_string_append(context->string, "env {\n"); + g_ptr_array_foreach(trace->environment, + (GFunc)append_env_field_metadata, context); + g_string_append(context->string, "};\n\n"); +} + +char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace) +{ + char *metadata = NULL; + struct metadata_context *context = NULL; + int err = 0; + size_t i; + + if (!trace) { + goto end; + } + + context = g_new0(struct metadata_context, 1); + if (!context) { + goto end; + } + + context->field_name = g_string_sized_new(DEFAULT_IDENTIFIER_SIZE); + context->string = g_string_sized_new(DEFAULT_METADATA_STRING_SIZE); + g_string_append(context->string, "/* CTF 1.8 */\n\n"); + if (append_trace_metadata(trace, context)) { + goto error; + } + append_env_metadata(trace, context); + g_ptr_array_foreach(trace->clocks, + (GFunc)bt_ctf_clock_serialize, context); + + for (i = 0; i < trace->stream_classes->len; i++) { + err = bt_ctf_stream_class_serialize( + trace->stream_classes->pdata[i], context); + if (err) { + goto error; + } + } + + metadata = context->string->str; +error: + g_string_free(context->string, err ? TRUE : FALSE); + g_string_free(context->field_name, TRUE); + g_free(context); +end: + return metadata; +} + +int bt_ctf_trace_set_byte_order(struct bt_ctf_trace *trace, + enum bt_ctf_byte_order byte_order) +{ + int ret = 0; + int internal_byte_order; + + if (!trace || trace->frozen) { + ret = -1; + goto end; + } + + switch (byte_order) { + case BT_CTF_BYTE_ORDER_NATIVE: + internal_byte_order = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? + LITTLE_ENDIAN : BIG_ENDIAN; + break; + case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: + internal_byte_order = LITTLE_ENDIAN; + break; + case BT_CTF_BYTE_ORDER_BIG_ENDIAN: + case BT_CTF_BYTE_ORDER_NETWORK: + internal_byte_order = BIG_ENDIAN; + break; + default: + ret = -1; + goto end; + } + + trace->byte_order = internal_byte_order; + if (trace->trace_packet_header_type || + trace->trace_packet_header) { + init_trace_packet_header(trace); + } +end: + return ret; +} + +void bt_ctf_trace_get(struct bt_ctf_trace *trace) +{ + if (!trace) { + return; + } + + bt_ctf_ref_get(&trace->ref_count); +} + +void bt_ctf_trace_put(struct bt_ctf_trace *trace) +{ + if (!trace) { + return; + } + + bt_ctf_ref_put(&trace->ref_count, bt_ctf_trace_destroy); +} + +BT_HIDDEN +int validate_identifier(const char *input_string) +{ + int ret = 0; + char *string = NULL; + char *save_ptr, *token; + + if (!input_string || input_string[0] == '\0') { + ret = -1; + goto end; + } + + string = strdup(input_string); + if (!string) { + ret = -1; + goto end; + } + + token = strtok_r(string, " ", &save_ptr); + while (token) { + if (g_hash_table_lookup_extended(reserved_keywords_set, + GINT_TO_POINTER(g_quark_from_string(token)), + NULL, NULL)) { + ret = -1; + goto end; + } + + token = strtok_r(NULL, " ", &save_ptr); + } +end: + free(string); + return ret; +} + +BT_HIDDEN +struct bt_ctf_field_type *get_field_type(enum field_type_alias alias) +{ + unsigned int alignment, size; + struct bt_ctf_field_type *field_type; + + if (alias >= NR_FIELD_TYPE_ALIAS) { + return NULL; + } + + alignment = field_type_aliases_alignments[alias]; + size = field_type_aliases_sizes[alias]; + field_type = bt_ctf_field_type_integer_create(size); + bt_ctf_field_type_set_alignment(field_type, alignment); + return field_type; +} + +static +int init_trace_packet_header(struct bt_ctf_trace *trace) +{ + size_t i; + int ret = 0; + struct bt_ctf_field *trace_packet_header = NULL, + *magic = NULL, *uuid_array = NULL; + struct bt_ctf_field_type *_uint32_t = + get_field_type(FIELD_TYPE_ALIAS_UINT32_T); + struct bt_ctf_field_type *_uint8_t = + get_field_type(FIELD_TYPE_ALIAS_UINT8_T); + struct bt_ctf_field_type *trace_packet_header_type = + bt_ctf_field_type_structure_create(); + struct bt_ctf_field_type *uuid_array_type = + bt_ctf_field_type_array_create(_uint8_t, 16); + + if (!trace_packet_header_type || !uuid_array_type) { + ret = -1; + goto end; + } + + ret = bt_ctf_field_type_set_byte_order(_uint32_t, + (trace->byte_order == LITTLE_ENDIAN ? + BT_CTF_BYTE_ORDER_LITTLE_ENDIAN : + BT_CTF_BYTE_ORDER_BIG_ENDIAN)); + if (ret) { + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, + _uint32_t, "magic"); + if (ret) { + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, + uuid_array_type, "uuid"); + if (ret) { + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, + _uint32_t, "stream_id"); + if (ret) { + goto end; + } + + trace_packet_header = bt_ctf_field_create(trace_packet_header_type); + if (!trace_packet_header) { + ret = -1; + goto end; + } + + magic = bt_ctf_field_structure_get_field(trace_packet_header, "magic"); + ret = bt_ctf_field_unsigned_integer_set_value(magic, 0xC1FC1FC1); + if (ret) { + goto end; + } + + uuid_array = bt_ctf_field_structure_get_field(trace_packet_header, + "uuid"); + for (i = 0; i < 16; i++) { + struct bt_ctf_field *uuid_element = + bt_ctf_field_array_get_field(uuid_array, i); + ret = bt_ctf_field_unsigned_integer_set_value(uuid_element, + trace->uuid[i]); + bt_ctf_field_put(uuid_element); + if (ret) { + goto end; + } + } + + bt_ctf_field_type_put(trace->trace_packet_header_type); + bt_ctf_field_put(trace->trace_packet_header); + trace->trace_packet_header_type = trace_packet_header_type; + trace->trace_packet_header = trace_packet_header; +end: + bt_ctf_field_type_put(uuid_array_type); + bt_ctf_field_type_put(_uint32_t); + bt_ctf_field_type_put(_uint8_t); + bt_ctf_field_put(magic); + bt_ctf_field_put(uuid_array); + if (ret) { + bt_ctf_field_type_put(trace_packet_header_type); + bt_ctf_field_put(trace_packet_header); + } + + return ret; +} + +static +void environment_variable_destroy(struct environment_variable *var) +{ + g_string_free(var->name, TRUE); + g_string_free(var->value, TRUE); + g_free(var); +} + +static __attribute__((constructor)) +void trace_init(void) +{ + size_t i; + const size_t reserved_keywords_count = + sizeof(reserved_keywords_str) / sizeof(char *); + + global_data_refcount++; + if (init_done) { + return; + } + + reserved_keywords_set = g_hash_table_new(g_direct_hash, g_direct_equal); + for (i = 0; i < reserved_keywords_count; i++) { + gpointer quark = GINT_TO_POINTER(g_quark_from_string( + reserved_keywords_str[i])); + + g_hash_table_insert(reserved_keywords_set, quark, quark); + } + + init_done = 1; +} + +static __attribute__((destructor)) +void trace_finalize(void) +{ + if (--global_data_refcount == 0) { + g_hash_table_destroy(reserved_keywords_set); + } +} diff --git a/formats/ctf/writer/writer.c b/formats/ctf/writer/writer.c index 460ac81c..7b876617 100644 --- a/formats/ctf/writer/writer.c +++ b/formats/ctf/writer/writer.c @@ -26,8 +26,6 @@ * SOFTWARE. */ -#include -#include #include #include #include @@ -35,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -45,51 +42,15 @@ #include #include -#define DEFAULT_IDENTIFIER_SIZE 128 -#define DEFAULT_METADATA_STRING_SIZE 4096 - -static -void environment_variable_destroy(struct environment_variable *var); static void bt_ctf_writer_destroy(struct bt_ctf_ref *ref); static -int init_trace_packet_header(struct bt_ctf_writer *writer); -static int create_stream_file(struct bt_ctf_writer *writer, struct bt_ctf_stream *stream); static void stream_flush_cb(struct bt_ctf_stream *stream, struct bt_ctf_writer *writer); -static -const char * const reserved_keywords_str[] = {"align", "callsite", - "const", "char", "clock", "double", "enum", "env", "event", - "floating_point", "float", "integer", "int", "long", "short", "signed", - "stream", "string", "struct", "trace", "typealias", "typedef", - "unsigned", "variant", "void" "_Bool", "_Complex", "_Imaginary"}; - -static -const unsigned int field_type_aliases_alignments[] = { - [FIELD_TYPE_ALIAS_UINT5_T] = 1, - [FIELD_TYPE_ALIAS_UINT8_T ... FIELD_TYPE_ALIAS_UINT16_T] = 8, - [FIELD_TYPE_ALIAS_UINT27_T] = 1, - [FIELD_TYPE_ALIAS_UINT32_T ... FIELD_TYPE_ALIAS_UINT64_T] = 8, -}; - -static -const unsigned int field_type_aliases_sizes[] = { - [FIELD_TYPE_ALIAS_UINT5_T] = 5, - [FIELD_TYPE_ALIAS_UINT8_T] = 8, - [FIELD_TYPE_ALIAS_UINT16_T] = 16, - [FIELD_TYPE_ALIAS_UINT27_T] = 27, - [FIELD_TYPE_ALIAS_UINT32_T] = 32, - [FIELD_TYPE_ALIAS_UINT64_T] = 64, -}; - -static GHashTable *reserved_keywords_set; -static int init_done; -static int global_data_refcount; - struct bt_ctf_writer *bt_ctf_writer_create(const char *path) { struct bt_ctf_writer *writer = NULL; @@ -103,13 +64,17 @@ struct bt_ctf_writer *bt_ctf_writer_create(const char *path) goto error; } - bt_ctf_writer_set_byte_order(writer, BT_CTF_BYTE_ORDER_NATIVE); bt_ctf_ref_init(&writer->ref_count); writer->path = g_string_new(path); if (!writer->path) { goto error_destroy; } + writer->trace = bt_ctf_trace_create(); + if (!writer->trace) { + goto error_destroy; + } + /* Create trace directory if necessary and open a metadata file */ if (g_mkdir_with_parents(path, S_IRWXU | S_IRWXG)) { perror("g_mkdir_with_parents"); @@ -125,26 +90,9 @@ struct bt_ctf_writer *bt_ctf_writer_create(const char *path) writer->metadata_fd = openat(writer->trace_dir_fd, "metadata", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - writer->environment = g_ptr_array_new_with_free_func( - (GDestroyNotify)environment_variable_destroy); - writer->clocks = g_ptr_array_new_with_free_func( - (GDestroyNotify)bt_ctf_clock_put); - writer->streams = g_ptr_array_new_with_free_func( - (GDestroyNotify)bt_ctf_stream_put); - writer->stream_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify)bt_ctf_stream_class_put); - if (!writer->environment || !writer->clocks || - !writer->stream_classes || !writer->streams) { - goto error_destroy; - } - - /* Generate a trace UUID */ - uuid_generate(writer->uuid); - if (init_trace_packet_header(writer)) { - goto error_destroy; - } return writer; + error_destroy: unlinkat(writer->trace_dir_fd, "metadata", 0); bt_ctf_writer_destroy(&writer->ref_count); @@ -170,44 +118,22 @@ void bt_ctf_writer_destroy(struct bt_ctf_ref *ref) if (writer->trace_dir_fd > 0) { if (close(writer->trace_dir_fd)) { perror("close"); - abort(); } } if (writer->metadata_fd > 0) { if (close(writer->metadata_fd)) { perror("close"); - abort(); } } - if (writer->environment) { - g_ptr_array_free(writer->environment, TRUE); - } - - if (writer->clocks) { - g_ptr_array_free(writer->clocks, TRUE); - } - - if (writer->streams) { - g_ptr_array_free(writer->streams, TRUE); - } - - if (writer->stream_classes) { - g_ptr_array_free(writer->stream_classes, TRUE); - } - - bt_ctf_field_type_put(writer->trace_packet_header_type); - bt_ctf_field_put(writer->trace_packet_header); + bt_ctf_trace_put(writer->trace); g_free(writer); } struct bt_ctf_stream *bt_ctf_writer_create_stream(struct bt_ctf_writer *writer, struct bt_ctf_stream_class *stream_class) { - int ret; - int stream_class_found = 0; - size_t i; int stream_fd; struct bt_ctf_stream *stream = NULL; @@ -215,14 +141,7 @@ struct bt_ctf_stream *bt_ctf_writer_create_stream(struct bt_ctf_writer *writer, goto error; } - ret = bt_ctf_stream_class_set_byte_order(stream_class, - writer->byte_order == LITTLE_ENDIAN ? - BT_CTF_BYTE_ORDER_LITTLE_ENDIAN : BT_CTF_BYTE_ORDER_BIG_ENDIAN); - if (ret) { - goto error; - } - - stream = bt_ctf_stream_create(stream_class); + stream = bt_ctf_trace_create_stream(writer->trace, stream_class); if (!stream) { goto error; } @@ -234,37 +153,9 @@ struct bt_ctf_stream *bt_ctf_writer_create_stream(struct bt_ctf_writer *writer, bt_ctf_stream_set_flush_callback(stream, (flush_func)stream_flush_cb, writer); - - for (i = 0; i < writer->stream_classes->len; i++) { - if (writer->stream_classes->pdata[i] == stream->stream_class) { - stream_class_found = 1; - } - } - - if (!stream_class_found) { - int64_t stream_id = bt_ctf_stream_class_get_id(stream_class); - if (stream_id < 0) { - if (bt_ctf_stream_class_set_id(stream->stream_class, - writer->next_stream_id++)) { - goto error; - } - } - - for (i = 0; i < writer->stream_classes->len; i++) { - if (stream_id == bt_ctf_stream_class_get_id( - writer->stream_classes->pdata[i])) { - /* Duplicate stream id found */ - goto error; - } - } - bt_ctf_stream_class_get(stream->stream_class); - g_ptr_array_add(writer->stream_classes, stream->stream_class); - } - - bt_ctf_stream_get(stream); - g_ptr_array_add(writer->streams, stream); writer->frozen = 1; return stream; + error: bt_ctf_stream_put(stream); return NULL; @@ -274,199 +165,44 @@ int bt_ctf_writer_add_environment_field(struct bt_ctf_writer *writer, const char *name, const char *value) { - struct environment_variable *var = NULL; - char *escaped_value = NULL; - int ret = 0; - - if (!writer || !name || !value || validate_identifier(name)) { - ret = -1; - goto error; - } - - if (strchr(name, ' ')) { - ret = -1; - goto error; - } + int ret = -1; - var = g_new0(struct environment_variable, 1); - if (!var) { - ret = -1; - goto error; - } - - escaped_value = g_strescape(value, NULL); - if (!escaped_value) { - ret = -1; - goto error; - } - - var->name = g_string_new(name); - var->value = g_string_new(escaped_value); - g_free(escaped_value); - if (!var->name || !var->value) { - ret = -1; - goto error; - } - - g_ptr_array_add(writer->environment, var); - return ret; - -error: - if (var && var->name) { - g_string_free(var->name, TRUE); - } - - if (var && var->value) { - g_string_free(var->value, TRUE); + if (!writer || !name || !value) { + goto end; } - g_free(var); + ret = bt_ctf_trace_add_environment_field(writer->trace, + name, value); +end: return ret; } int bt_ctf_writer_add_clock(struct bt_ctf_writer *writer, struct bt_ctf_clock *clock) { - int ret = 0; - struct search_query query = { .value = clock, .found = 0 }; + int ret = -1; if (!writer || !clock) { - ret = -1; - goto end; - } - - /* Check for duplicate clocks */ - g_ptr_array_foreach(writer->clocks, value_exists, &query); - if (query.found) { - ret = -1; - goto end; - } - - bt_ctf_clock_get(clock); - g_ptr_array_add(writer->clocks, clock); -end: - return ret; -} - -BT_HIDDEN -const char *get_byte_order_string(int byte_order) -{ - const char *string; - - switch (byte_order) { - case LITTLE_ENDIAN: - string = "le"; - break; - case BIG_ENDIAN: - string = "be"; - break; - default: - string = "unknown"; - break; - } - - return string; -} - -static -int append_trace_metadata(struct bt_ctf_writer *writer, - struct metadata_context *context) -{ - unsigned char *uuid = writer->uuid; - int ret; - - g_string_append(context->string, "trace {\n"); - - g_string_append(context->string, "\tmajor = 1;\n"); - g_string_append(context->string, "\tminor = 8;\n"); - - g_string_append_printf(context->string, - "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n", - uuid[0], uuid[1], uuid[2], uuid[3], - uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], - uuid[12], uuid[13], uuid[14], uuid[15]); - g_string_append_printf(context->string, "\tbyte_order = %s;\n", - get_byte_order_string(writer->byte_order)); - - g_string_append(context->string, "\tpacket.header := "); - context->current_indentation_level++; - g_string_assign(context->field_name, ""); - ret = bt_ctf_field_type_serialize(writer->trace_packet_header_type, - context); - if (ret) { goto end; } - context->current_indentation_level--; - g_string_append(context->string, ";\n};\n\n"); + ret = bt_ctf_trace_add_clock(writer->trace, clock); end: return ret; } -static -void append_env_field_metadata(struct environment_variable *var, - struct metadata_context *context) -{ - g_string_append_printf(context->string, "\t%s = \"%s\";\n", - var->name->str, var->value->str); -} - -static -void append_env_metadata(struct bt_ctf_writer *writer, - struct metadata_context *context) -{ - if (writer->environment->len == 0) { - return; - } - - g_string_append(context->string, "env {\n"); - g_ptr_array_foreach(writer->environment, - (GFunc)append_env_field_metadata, context); - g_string_append(context->string, "};\n\n"); -} - char *bt_ctf_writer_get_metadata_string(struct bt_ctf_writer *writer) { - char *metadata = NULL; - struct metadata_context *context = NULL; - int err = 0; - size_t i; + char *metadata_string = NULL; if (!writer) { goto end; } - context = g_new0(struct metadata_context, 1); - if (!context) { - goto end; - } - - context->field_name = g_string_sized_new(DEFAULT_IDENTIFIER_SIZE); - context->string = g_string_sized_new(DEFAULT_METADATA_STRING_SIZE); - g_string_append(context->string, "/* CTF 1.8 */\n\n"); - if (append_trace_metadata(writer, context)) { - goto error; - } - append_env_metadata(writer, context); - g_ptr_array_foreach(writer->clocks, - (GFunc)bt_ctf_clock_serialize, context); - - for (i = 0; i < writer->stream_classes->len; i++) { - err = bt_ctf_stream_class_serialize( - writer->stream_classes->pdata[i], context); - if (err) { - goto error; - } - } - - metadata = context->string->str; -error: - g_string_free(context->string, err ? TRUE : FALSE); - g_string_free(context->field_name, TRUE); - g_free(context); + metadata_string = bt_ctf_trace_get_metadata_string( + writer->trace); end: - return metadata; + return metadata_string; } void bt_ctf_writer_flush_metadata(struct bt_ctf_writer *writer) @@ -478,7 +214,8 @@ void bt_ctf_writer_flush_metadata(struct bt_ctf_writer *writer) goto end; } - metadata_string = bt_ctf_writer_get_metadata_string(writer); + metadata_string = bt_ctf_trace_get_metadata_string( + writer->trace); if (!metadata_string) { goto end; } @@ -507,35 +244,14 @@ int bt_ctf_writer_set_byte_order(struct bt_ctf_writer *writer, enum bt_ctf_byte_order byte_order) { int ret = 0; - int internal_byte_order; if (!writer || writer->frozen) { ret = -1; goto end; } - switch (byte_order) { - case BT_CTF_BYTE_ORDER_NATIVE: - internal_byte_order = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? - LITTLE_ENDIAN : BIG_ENDIAN; - break; - case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: - internal_byte_order = LITTLE_ENDIAN; - break; - case BT_CTF_BYTE_ORDER_BIG_ENDIAN: - case BT_CTF_BYTE_ORDER_NETWORK: - internal_byte_order = BIG_ENDIAN; - break; - default: - ret = -1; - goto end; - } - - writer->byte_order = internal_byte_order; - if (writer->trace_packet_header_type || - writer->trace_packet_header) { - init_trace_packet_header(writer); - } + ret = bt_ctf_trace_set_byte_order(writer->trace, + byte_order); end: return ret; } @@ -558,155 +274,6 @@ void bt_ctf_writer_put(struct bt_ctf_writer *writer) bt_ctf_ref_put(&writer->ref_count, bt_ctf_writer_destroy); } -BT_HIDDEN -int validate_identifier(const char *input_string) -{ - int ret = 0; - char *string = NULL; - char *save_ptr, *token; - - if (!input_string || input_string[0] == '\0') { - ret = -1; - goto end; - } - - string = strdup(input_string); - if (!string) { - ret = -1; - goto end; - } - - token = strtok_r(string, " ", &save_ptr); - while (token) { - if (g_hash_table_lookup_extended(reserved_keywords_set, - GINT_TO_POINTER(g_quark_from_string(token)), - NULL, NULL)) { - ret = -1; - goto end; - } - - token = strtok_r(NULL, " ", &save_ptr); - } -end: - free(string); - return ret; -} - -BT_HIDDEN -struct bt_ctf_field_type *get_field_type(enum field_type_alias alias) -{ - unsigned int alignment, size; - struct bt_ctf_field_type *field_type; - - if (alias >= NR_FIELD_TYPE_ALIAS) { - return NULL; - } - - alignment = field_type_aliases_alignments[alias]; - size = field_type_aliases_sizes[alias]; - field_type = bt_ctf_field_type_integer_create(size); - bt_ctf_field_type_set_alignment(field_type, alignment); - return field_type; -} - -static -int init_trace_packet_header(struct bt_ctf_writer *writer) -{ - size_t i; - int ret = 0; - struct bt_ctf_field *trace_packet_header = NULL, - *magic = NULL, *uuid_array = NULL; - struct bt_ctf_field_type *_uint32_t = - get_field_type(FIELD_TYPE_ALIAS_UINT32_T); - struct bt_ctf_field_type *_uint8_t = - get_field_type(FIELD_TYPE_ALIAS_UINT8_T); - struct bt_ctf_field_type *trace_packet_header_type = - bt_ctf_field_type_structure_create(); - struct bt_ctf_field_type *uuid_array_type = - bt_ctf_field_type_array_create(_uint8_t, 16); - - if (!trace_packet_header_type || !uuid_array_type) { - ret = -1; - goto end; - } - - ret = bt_ctf_field_type_set_byte_order(_uint32_t, - (writer->byte_order == LITTLE_ENDIAN ? - BT_CTF_BYTE_ORDER_LITTLE_ENDIAN : - BT_CTF_BYTE_ORDER_BIG_ENDIAN)); - if (ret) { - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, - _uint32_t, "magic"); - if (ret) { - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, - uuid_array_type, "uuid"); - if (ret) { - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, - _uint32_t, "stream_id"); - if (ret) { - goto end; - } - - trace_packet_header = bt_ctf_field_create(trace_packet_header_type); - if (!trace_packet_header) { - ret = -1; - goto end; - } - - magic = bt_ctf_field_structure_get_field(trace_packet_header, "magic"); - ret = bt_ctf_field_unsigned_integer_set_value(magic, 0xC1FC1FC1); - if (ret) { - goto end; - } - - uuid_array = bt_ctf_field_structure_get_field(trace_packet_header, - "uuid"); - for (i = 0; i < 16; i++) { - struct bt_ctf_field *uuid_element = - bt_ctf_field_array_get_field(uuid_array, i); - ret = bt_ctf_field_unsigned_integer_set_value(uuid_element, - writer->uuid[i]); - bt_ctf_field_put(uuid_element); - if (ret) { - goto end; - } - } - - bt_ctf_field_type_put(writer->trace_packet_header_type); - bt_ctf_field_put(writer->trace_packet_header); - writer->trace_packet_header_type = trace_packet_header_type; - writer->trace_packet_header = trace_packet_header; -end: - bt_ctf_field_type_put(uuid_array_type); - bt_ctf_field_type_put(_uint32_t); - bt_ctf_field_type_put(_uint8_t); - bt_ctf_field_put(magic); - bt_ctf_field_put(uuid_array); - if (ret) { - bt_ctf_field_type_put(trace_packet_header_type); - bt_ctf_field_put(trace_packet_header); - } - - return ret; -} - -static -void environment_variable_destroy(struct environment_variable *var) -{ - g_string_free(var->name, TRUE); - g_string_free(var->value, TRUE); - g_free(var); -} - static int create_stream_file(struct bt_ctf_writer *writer, struct bt_ctf_stream *stream) @@ -734,41 +301,10 @@ void stream_flush_cb(struct bt_ctf_stream *stream, struct bt_ctf_writer *writer) } stream_id = bt_ctf_field_structure_get_field( - writer->trace_packet_header, "stream_id"); + writer->trace->trace_packet_header, "stream_id"); bt_ctf_field_unsigned_integer_set_value(stream_id, stream->stream_class->id); bt_ctf_field_put(stream_id); /* Write the trace_packet_header */ - bt_ctf_field_serialize(writer->trace_packet_header, &stream->pos); -} - -static __attribute__((constructor)) -void writer_init(void) -{ - size_t i; - const size_t reserved_keywords_count = - sizeof(reserved_keywords_str) / sizeof(char *); - - global_data_refcount++; - if (init_done) { - return; - } - - reserved_keywords_set = g_hash_table_new(g_direct_hash, g_direct_equal); - for (i = 0; i < reserved_keywords_count; i++) { - gpointer quark = GINT_TO_POINTER(g_quark_from_string( - reserved_keywords_str[i])); - - g_hash_table_insert(reserved_keywords_set, quark, quark); - } - - init_done = 1; -} - -static __attribute__((destructor)) -void writer_finalize(void) -{ - if (--global_data_refcount == 0) { - g_hash_table_destroy(reserved_keywords_set); - } + bt_ctf_field_serialize(writer->trace->trace_packet_header, &stream->pos); } diff --git a/include/Makefile.am b/include/Makefile.am index ea09895e..35af83ae 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -27,7 +27,8 @@ babeltracectfirinclude_HEADERS = \ babeltrace/ctf-ir/event-types.h \ babeltrace/ctf-ir/event.h \ babeltrace/ctf-ir/stream.h \ - babeltrace/ctf-ir/stream-class.h + babeltrace/ctf-ir/stream-class.h \ + babeltrace/ctf-ir/trace.h noinst_HEADERS = \ babeltrace/align.h \ diff --git a/include/babeltrace/ctf-ir/clock-internal.h b/include/babeltrace/ctf-ir/clock-internal.h index dc4d02ee..89c271d3 100644 --- a/include/babeltrace/ctf-ir/clock-internal.h +++ b/include/babeltrace/ctf-ir/clock-internal.h @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include #include diff --git a/include/babeltrace/ctf-ir/event-types-internal.h b/include/babeltrace/ctf-ir/event-types-internal.h index 8d0477b5..58a94045 100644 --- a/include/babeltrace/ctf-ir/event-types-internal.h +++ b/include/babeltrace/ctf-ir/event-types-internal.h @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/babeltrace/ctf-ir/stream-internal.h b/include/babeltrace/ctf-ir/stream-internal.h index 9214abb8..afbb9ff5 100644 --- a/include/babeltrace/ctf-ir/stream-internal.h +++ b/include/babeltrace/ctf-ir/stream-internal.h @@ -27,6 +27,7 @@ * SOFTWARE. */ +#include #include #include #include diff --git a/include/babeltrace/ctf-ir/trace.h b/include/babeltrace/ctf-ir/trace.h new file mode 100644 index 00000000..1be85cfd --- /dev/null +++ b/include/babeltrace/ctf-ir/trace.h @@ -0,0 +1,145 @@ +#ifndef BABELTRACE_CTF_IR_TRACE_H +#define BABELTRACE_CTF_IR_TRACE_H + +/* + * BabelTrace - CTF IR: Trace + * + * Copyright 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_ctf_trace; +struct bt_ctf_stream; +struct bt_ctf_stream_class; +struct bt_ctf_clock; + +/* + * bt_ctf_trace_create: create a trace instance. + * + * Allocate a new trace + * + * Returns a new trace on success, NULL on error. + */ +extern struct bt_ctf_trace *bt_ctf_trace_create(void); + +/* + * bt_ctf_trace_create_stream: create a stream instance. + * + * Allocate a new stream instance and register it to the trace. The creation of + * a stream sets its reference count to 1. + * + * @param trace Trace instance. + * @param stream_class Stream class to instantiate. + * + * Returns a new stream on success, NULL on error. + */ +extern struct bt_ctf_stream *bt_ctf_trace_create_stream( + struct bt_ctf_trace *trace, + struct bt_ctf_stream_class *stream_class); + +/* + * bt_ctf_trace_add_environment_field: add an environment field to the trace. + * + * Add an environment field to the trace. The name and value parameters are + * copied. + * + * @param trace Trace instance. + * @param name Name of the environment field (will be copied). + * @param value Value of the environment field (will be copied). + * + * Returns 0 on success, a negative value on error. + */ +extern int bt_ctf_trace_add_environment_field(struct bt_ctf_trace *trace, + const char *name, + const char *value); + +/* + * bt_ctf_trace_add_clock: add a clock to the trace. + * + * Add a clock to the trace. Clocks assigned to stream classes must be + * added to the trace beforehand. + * + * @param trace Trace instance. + * @param clock Clock to add to the trace. + * + * Returns 0 on success, a negative value on error. + */ +extern int bt_ctf_trace_add_clock(struct bt_ctf_trace *trace, + struct bt_ctf_clock *clock); + +/* + * bt_ctf_trace_get_metadata_string: get metadata string. + * + * Get the trace's TSDL metadata. The caller assumes the ownership of the + * returned string. + * + * @param trace Trace instance. + * + * Returns the metadata string on success, NULL on error. + */ +extern char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace); + +/* + * bt_ctf_trace_set_byte_order: set a field type's byte order. + * + * Set the trace's byte order. Defaults to BT_CTF_BYTE_ORDER_NATIVE, + * the host machine's endianness. + * + * @param trace Trace instance. + * @param byte_order Trace's byte order. + * + * Returns 0 on success, a negative value on error. + */ +extern int bt_ctf_trace_set_byte_order(struct bt_ctf_trace *trace, + enum bt_ctf_byte_order byte_order); + +/* + * bt_ctf_trace_get and bt_ctf_trace_put: increment and decrement the + * trace's reference count. + * + * These functions ensure that the trace won't be destroyed while it + * is in use. The same number of get and put (plus one extra put to + * release the initial reference done at creation) have to be done to + * destroy a trace. + * + * When the trace's reference count is decremented to 0 by a + * bt_ctf_trace_put, the trace is freed. + * + * @param trace Trace instance. + */ +extern void bt_ctf_trace_get(struct bt_ctf_trace *trace); +extern void bt_ctf_trace_put(struct bt_ctf_trace *trace); + +#ifdef __cplusplus +} +#endif + +#endif /* BABELTRACE_CTF_IR_TRACE_H */ diff --git a/include/babeltrace/ctf-writer/writer-internal.h b/include/babeltrace/ctf-writer/writer-internal.h index cf7e399c..388ff0d1 100644 --- a/include/babeltrace/ctf-writer/writer-internal.h +++ b/include/babeltrace/ctf-writer/writer-internal.h @@ -29,59 +29,19 @@ #include #include -#include -#include #include #include #include #include -#include - -enum field_type_alias { - FIELD_TYPE_ALIAS_UINT5_T = 0, - FIELD_TYPE_ALIAS_UINT8_T, - FIELD_TYPE_ALIAS_UINT16_T, - FIELD_TYPE_ALIAS_UINT27_T, - FIELD_TYPE_ALIAS_UINT32_T, - FIELD_TYPE_ALIAS_UINT64_T, - NR_FIELD_TYPE_ALIAS, -}; +#include struct bt_ctf_writer { struct bt_ctf_ref ref_count; int frozen; /* Protects attributes that can't be changed mid-trace */ + struct bt_ctf_trace *trace; GString *path; - uuid_t uuid; - int byte_order; int trace_dir_fd; int metadata_fd; - GPtrArray *environment; /* Array of pointers to environment_variable */ - GPtrArray *clocks; /* Array of pointers to bt_ctf_clock */ - GPtrArray *stream_classes; /* Array of pointers to bt_ctf_stream_class */ - GPtrArray *streams; /* Array of pointers to bt_ctf_stream */ - struct bt_ctf_field_type *trace_packet_header_type; - struct bt_ctf_field *trace_packet_header; - uint64_t next_stream_id; }; -struct environment_variable { - GString *name, *value; -}; - -struct metadata_context { - GString *string; - GString *field_name; - unsigned int current_indentation_level; -}; - -/* Checks that the string does not contain a reserved keyword */ -BT_HIDDEN -int validate_identifier(const char *string); - -BT_HIDDEN -const char *get_byte_order_string(int byte_order); - -BT_HIDDEN -struct bt_ctf_field_type *get_field_type(enum field_type_alias alias); - #endif /* BABELTRACE_CTF_WRITER_WRITER_INTERNAL_H */ -- 2.34.1