/* * Copyright 2013, 2014 Jérémie Galarneau * Copyright 2017-2018 Philippe Proulx * * 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. */ #define BT_LOG_TAG "CTF-WRITER/STREAM-CLASS" #include "logging.h" #include #include #include #include #include #include #include "common/align.h" #include "common/assert.h" #include "compat/compiler.h" #include "compat/endian.h" #include "assert-pre.h" #include "clock-class.h" #include "event-class.h" #include "event.h" #include "fields.h" #include "field-types.h" #include "field-wrapper.h" #include "stream-class.h" #include "utils.h" #include "validation.h" #include "visitor.h" #include "writer.h" BT_HIDDEN int bt_ctf_stream_class_common_initialize(struct bt_ctf_stream_class_common *stream_class, const char *name, bt_ctf_object_release_func release_func) { BT_LOGD("Initializing common stream class object: name=\"%s\"", name); bt_ctf_object_init_shared_with_parent(&stream_class->base, release_func); stream_class->name = g_string_new(name); stream_class->event_classes = g_ptr_array_new_with_free_func( (GDestroyNotify) bt_ctf_object_try_spec_release); if (!stream_class->event_classes) { BT_LOGE_STR("Failed to allocate a GPtrArray."); goto error; } stream_class->event_classes_ht = g_hash_table_new_full(g_int64_hash, g_int64_equal, g_free, NULL); if (!stream_class->event_classes_ht) { BT_LOGE_STR("Failed to allocate a GHashTable."); goto error; } BT_LOGD("Initialized common stream class object: addr=%p, name=\"%s\"", stream_class, name); return 0; error: return -1; } BT_HIDDEN void bt_ctf_stream_class_common_finalize(struct bt_ctf_stream_class_common *stream_class) { BT_LOGD("Finalizing common stream class: addr=%p, name=\"%s\", id=%" PRId64, stream_class, bt_ctf_stream_class_common_get_name(stream_class), bt_ctf_stream_class_common_get_id(stream_class)); bt_ctf_object_put_ref(stream_class->clock_class); if (stream_class->event_classes_ht) { g_hash_table_destroy(stream_class->event_classes_ht); } if (stream_class->event_classes) { BT_LOGD_STR("Destroying event classes."); g_ptr_array_free(stream_class->event_classes, TRUE); } if (stream_class->name) { g_string_free(stream_class->name, TRUE); } BT_LOGD_STR("Putting event header field type."); bt_ctf_object_put_ref(stream_class->event_header_field_type); BT_LOGD_STR("Putting packet context field type."); bt_ctf_object_put_ref(stream_class->packet_context_field_type); BT_LOGD_STR("Putting event context field type."); bt_ctf_object_put_ref(stream_class->event_context_field_type); } static void event_class_exists(gpointer element, gpointer query) { struct bt_ctf_event_class_common *event_class_a = element; struct bt_ctf_search_query *search_query = query; struct bt_ctf_event_class_common *event_class_b = search_query->value; int64_t id_a, id_b; if (search_query->value == element) { search_query->found = 1; goto end; } /* * Two event classes cannot share the same ID in a given * stream class. */ id_a = bt_ctf_event_class_common_get_id(event_class_a); id_b = bt_ctf_event_class_common_get_id(event_class_b); if (id_a < 0 || id_b < 0) { /* at least one ID is not set: will be automatically set later */ goto end; } if (id_a == id_b) { BT_LOGW("Event class with this ID already exists in the stream class: " "id=%" PRId64 ", name=\"%s\"", id_a, bt_ctf_event_class_common_get_name(event_class_a)); search_query->found = 1; goto end; } end: return; } BT_HIDDEN int bt_ctf_stream_class_common_add_event_class( struct bt_ctf_stream_class_common *stream_class, struct bt_ctf_event_class_common *event_class, bt_ctf_validation_flag_copy_field_type_func copy_field_type_func) { int ret = 0; int64_t *event_id = NULL; struct bt_ctf_trace_common *trace = NULL; struct bt_ctf_stream_class_common *old_stream_class = NULL; struct bt_ctf_validation_output validation_output = { 0 }; struct bt_ctf_field_type_common *packet_header_type = NULL; struct bt_ctf_field_type_common *packet_context_type = NULL; struct bt_ctf_field_type_common *event_header_type = NULL; struct bt_ctf_field_type_common *stream_event_ctx_type = NULL; struct bt_ctf_field_type_common *event_context_type = NULL; struct bt_ctf_field_type_common *event_payload_type = NULL; const enum bt_ctf_validation_flag validation_flags = BT_CTF_VALIDATION_FLAG_EVENT; struct bt_ctf_clock_class *expected_clock_class = NULL; struct bt_ctf_search_query query = { .value = event_class, .found = 0 }; BT_ASSERT_DBG(copy_field_type_func); if (!stream_class || !event_class) { BT_LOGW("Invalid parameter: stream class or event class is NULL: " "stream-class-addr=%p, event-class-addr=%p", stream_class, event_class); ret = -1; goto end; } BT_LOGD("Adding event class to stream class: " "stream-class-addr=%p, stream-class-name=\"%s\", " "stream-class-id=%" PRId64 ", event-class-addr=%p, " "event-class-name=\"%s\", event-class-id=%" PRId64, stream_class, bt_ctf_stream_class_common_get_name(stream_class), bt_ctf_stream_class_common_get_id(stream_class), event_class, bt_ctf_event_class_common_get_name(event_class), bt_ctf_event_class_common_get_id(event_class)); trace = bt_ctf_stream_class_common_borrow_trace(stream_class); if (stream_class->frozen) { /* * We only check that the event class to be added has a * single class which matches the stream class's * expected clock class if the stream class is frozen. * If it's not, then this event class is added "as is" * and the validation will be performed when calling * either bt_ctf_trace_add_stream_class() or * bt_ctf_event_create(). This is because the stream class's * field types (packet context, event header, event * context) could change before the next call to one of * those two functions. */ expected_clock_class = bt_ctf_object_get_ref(stream_class->clock_class); /* * At this point, `expected_clock_class` can be NULL, * and bt_ctf_event_class_validate_single_clock_class() * below can set it. */ ret = bt_ctf_event_class_common_validate_single_clock_class( event_class, &expected_clock_class); if (ret) { BT_LOGW("Event class contains a field type which is not " "recursively mapped to its stream class's " "expected clock class: " "stream-class-addr=%p, " "stream-class-id=%" PRId64 ", " "stream-class-name=\"%s\", " "expected-clock-class-addr=%p, " "expected-clock-class-name=\"%s\"", stream_class, bt_ctf_stream_class_common_get_id(stream_class), bt_ctf_stream_class_common_get_name(stream_class), expected_clock_class, expected_clock_class ? bt_ctf_clock_class_get_name(expected_clock_class) : NULL); goto end; } } event_id = g_new(int64_t, 1); if (!event_id) { BT_LOGE_STR("Failed to allocate one int64_t."); ret = -1; goto end; } /* Check for duplicate event classes */ g_ptr_array_foreach(stream_class->event_classes, event_class_exists, &query); if (query.found) { BT_LOGW_STR("Another event class part of this stream class has the same ID."); ret = -1; goto end; } old_stream_class = bt_ctf_event_class_common_borrow_stream_class(event_class); if (old_stream_class) { /* Event class is already associated to a stream class. */ BT_LOGW("Event class is already part of another stream class: " "event-class-stream-class-addr=%p, " "event-class-stream-class-name=\"%s\", " "event-class-stream-class-id=%" PRId64, old_stream_class, bt_ctf_stream_class_common_get_name(old_stream_class), bt_ctf_stream_class_common_get_id(old_stream_class)); ret = -1; goto end; } if (trace) { /* * If the stream class is associated with a trace, then * both those objects are frozen. Also, this event class * is about to be frozen. * * Therefore the event class must be validated here. * The trace and stream class should be valid at this * point. */ BT_ASSERT_DBG(trace->valid); BT_ASSERT_DBG(stream_class->valid); packet_header_type = bt_ctf_trace_common_borrow_packet_header_field_type(trace); packet_context_type = bt_ctf_stream_class_common_borrow_packet_context_field_type( stream_class); event_header_type = bt_ctf_stream_class_common_borrow_event_header_field_type( stream_class); stream_event_ctx_type = bt_ctf_stream_class_common_borrow_event_context_field_type( stream_class); event_context_type = bt_ctf_event_class_common_borrow_context_field_type( event_class); event_payload_type = bt_ctf_event_class_common_borrow_payload_field_type( event_class); ret = bt_ctf_validate_class_types( trace->environment, packet_header_type, packet_context_type, event_header_type, stream_event_ctx_type, event_context_type, event_payload_type, trace->valid, stream_class->valid, event_class->valid, &validation_output, validation_flags, copy_field_type_func); if (ret) { /* * This means something went wrong during the * validation process, not that the objects are * invalid. */ BT_LOGE("Failed to validate event class: ret=%d", ret); goto end; } if ((validation_output.valid_flags & validation_flags) != validation_flags) { /* Invalid event class */ BT_LOGW("Invalid trace, stream class, or event class: " "valid-flags=0x%x", validation_output.valid_flags); ret = -1; goto end; } } /* Only set an event ID if none was explicitly set before */ *event_id = bt_ctf_event_class_common_get_id(event_class); if (*event_id < 0) { BT_LOGT("Event class has no ID: automatically setting it: " "id=%" PRId64, stream_class->next_event_id); if (bt_ctf_event_class_common_set_id(event_class, stream_class->next_event_id)) { BT_LOGE("Cannot set event class's ID: id=%" PRId64, stream_class->next_event_id); ret = -1; goto end; } stream_class->next_event_id++; *event_id = stream_class->next_event_id; } bt_ctf_object_set_parent(&event_class->base, &stream_class->base); if (trace) { /* * At this point we know that the function will be * successful. Therefore we can replace the event * class's field types with what's in the validation * output structure and mark this event class as valid. */ bt_ctf_validation_replace_types(NULL, NULL, event_class, &validation_output, validation_flags); event_class->valid = 1; /* * Put what was not moved in * bt_ctf_validation_replace_types(). */ bt_ctf_validation_output_put_types(&validation_output); } /* Add to the event classes of the stream class */ g_ptr_array_add(stream_class->event_classes, event_class); g_hash_table_insert(stream_class->event_classes_ht, event_id, event_class); event_id = NULL; /* Freeze the event class */ bt_ctf_event_class_common_freeze(event_class); /* * It is safe to set the stream class's unique clock class * now if the stream class is frozen. */ if (stream_class->frozen && expected_clock_class) { BT_ASSERT_DBG(!stream_class->clock_class || stream_class->clock_class == expected_clock_class); BT_CTF_OBJECT_MOVE_REF(stream_class->clock_class, expected_clock_class); } BT_LOGD("Added event class to stream class: " "stream-class-addr=%p, stream-class-name=\"%s\", " "stream-class-id=%" PRId64 ", event-class-addr=%p, " "event-class-name=\"%s\", event-class-id=%" PRId64, stream_class, bt_ctf_stream_class_common_get_name(stream_class), bt_ctf_stream_class_common_get_id(stream_class), event_class, bt_ctf_event_class_common_get_name(event_class), bt_ctf_event_class_common_get_id(event_class)); end: bt_ctf_validation_output_put_types(&validation_output); bt_ctf_object_put_ref(expected_clock_class); g_free(event_id); return ret; } static int64_t get_event_class_count(void *element) { return bt_ctf_stream_class_get_event_class_count( (struct bt_ctf_stream_class *) element); } static void *get_event_class(void *element, int i) { return bt_ctf_stream_class_get_event_class_by_index( (struct bt_ctf_stream_class *) element, i); } static int visit_event_class(void *object, bt_ctf_visitor visitor,void *data) { struct bt_ctf_visitor_object obj = { .object = object, .type = BT_CTF_VISITOR_OBJECT_TYPE_EVENT_CLASS }; return visitor(&obj, data); } BT_HIDDEN int bt_ctf_stream_class_common_visit(struct bt_ctf_stream_class_common *stream_class, bt_ctf_visitor visitor, void *data) { int ret; struct bt_ctf_visitor_object obj = { .object = stream_class, .type = BT_CTF_VISITOR_OBJECT_TYPE_STREAM_CLASS }; if (!stream_class || !visitor) { BT_LOGW("Invalid parameter: stream class or visitor is NULL: " "stream-class-addr=%p, visitor=%p", stream_class, visitor); ret = -1; goto end; } ret = bt_ctf_visitor_helper(&obj, get_event_class_count, get_event_class, visit_event_class, visitor, data); BT_LOGT("bt_ctf_visitor_helper() returned: ret=%d", ret); end: return ret; } BT_HIDDEN int bt_ctf_stream_class_visit(struct bt_ctf_stream_class *stream_class, bt_ctf_visitor visitor, void *data) { return bt_ctf_stream_class_common_visit(BT_CTF_FROM_COMMON(stream_class), visitor, data); } BT_HIDDEN void bt_ctf_stream_class_common_freeze(struct bt_ctf_stream_class_common *stream_class) { if (!stream_class || stream_class->frozen) { return; } BT_LOGD("Freezing stream class: addr=%p, name=\"%s\", id=%" PRId64, stream_class, bt_ctf_stream_class_common_get_name(stream_class), bt_ctf_stream_class_common_get_id(stream_class)); stream_class->frozen = 1; bt_ctf_field_type_common_freeze(stream_class->event_header_field_type); bt_ctf_field_type_common_freeze(stream_class->packet_context_field_type); bt_ctf_field_type_common_freeze(stream_class->event_context_field_type); bt_ctf_clock_class_freeze(stream_class->clock_class); } BT_HIDDEN int bt_ctf_stream_class_common_validate_single_clock_class( struct bt_ctf_stream_class_common *stream_class, struct bt_ctf_clock_class **expected_clock_class) { int ret; uint64_t i; BT_ASSERT_DBG(stream_class); BT_ASSERT_DBG(expected_clock_class); ret = bt_ctf_field_type_common_validate_single_clock_class( stream_class->packet_context_field_type, expected_clock_class); if (ret) { BT_LOGW("Stream class's packet context field type " "is not recursively mapped to the " "expected clock class: " "stream-class-addr=%p, " "stream-class-name=\"%s\", " "stream-class-id=%" PRId64 ", " "ft-addr=%p", stream_class, bt_ctf_stream_class_common_get_name(stream_class), stream_class->id, stream_class->packet_context_field_type); goto end; } ret = bt_ctf_field_type_common_validate_single_clock_class( stream_class->event_header_field_type, expected_clock_class); if (ret) { BT_LOGW("Stream class's event header field type " "is not recursively mapped to the " "expected clock class: " "stream-class-addr=%p, " "stream-class-name=\"%s\", " "stream-class-id=%" PRId64 ", " "ft-addr=%p", stream_class, bt_ctf_stream_class_common_get_name(stream_class), stream_class->id, stream_class->event_header_field_type); goto end; } ret = bt_ctf_field_type_common_validate_single_clock_class( stream_class->event_context_field_type, expected_clock_class); if (ret) { BT_LOGW("Stream class's event context field type " "is not recursively mapped to the " "expected clock class: " "stream-class-addr=%p, " "stream-class-name=\"%s\", " "stream-class-id=%" PRId64 ", " "ft-addr=%p", stream_class, bt_ctf_stream_class_common_get_name(stream_class), stream_class->id, stream_class->event_context_field_type); goto end; } for (i = 0; i < stream_class->event_classes->len; i++) { struct bt_ctf_event_class_common *event_class = g_ptr_array_index(stream_class->event_classes, i); BT_ASSERT_DBG(event_class); ret = bt_ctf_event_class_common_validate_single_clock_class( event_class, expected_clock_class); if (ret) { BT_LOGW("Stream class's event class contains a " "field type which is not recursively mapped to " "the expected clock class: " "stream-class-addr=%p, " "stream-class-name=\"%s\", " "stream-class-id=%" PRId64, stream_class, bt_ctf_stream_class_common_get_name(stream_class), stream_class->id); goto end; } } end: return ret; } static int init_event_header(struct bt_ctf_stream_class *stream_class) { int ret = 0; struct bt_ctf_field_type *event_header_type = bt_ctf_field_type_structure_create(); struct bt_ctf_field_type *_uint32_t = get_field_type(FIELD_TYPE_ALIAS_UINT32_T); struct bt_ctf_field_type *_uint64_t = get_field_type(FIELD_TYPE_ALIAS_UINT64_T); if (!event_header_type) { BT_LOGE_STR("Cannot create empty structure field type."); ret = -1; goto end; } ret = bt_ctf_field_type_structure_add_field(event_header_type, _uint32_t, "id"); if (ret) { BT_LOGE_STR("Cannot add `id` field to event header field type."); goto end; } ret = bt_ctf_field_type_structure_add_field(event_header_type, _uint64_t, "timestamp"); if (ret) { BT_LOGE_STR("Cannot add `timestamp` field to event header field type."); goto end; } bt_ctf_object_put_ref(stream_class->common.event_header_field_type); stream_class->common.event_header_field_type = (void *) event_header_type; event_header_type = NULL; end: if (ret) { bt_ctf_object_put_ref(event_header_type); } bt_ctf_object_put_ref(_uint32_t); bt_ctf_object_put_ref(_uint64_t); return ret; } static int init_packet_context(struct bt_ctf_stream_class *stream_class) { int ret = 0; struct bt_ctf_field_type *packet_context_type = bt_ctf_field_type_structure_create(); struct bt_ctf_field_type *_uint64_t = get_field_type(FIELD_TYPE_ALIAS_UINT64_T); struct bt_ctf_field_type *ts_begin_end_uint64_t = NULL; if (!packet_context_type) { BT_LOGE_STR("Cannot create empty structure field type."); ret = -1; goto end; } ts_begin_end_uint64_t = bt_ctf_field_type_copy(_uint64_t); if (!ts_begin_end_uint64_t) { BT_LOGE_STR("Cannot copy integer field type for `timestamp_begin` and `timestamp_end` fields."); ret = -1; goto end; } /* * We create a stream packet context as proposed in the CTF * specification. */ ret = bt_ctf_field_type_structure_add_field(packet_context_type, ts_begin_end_uint64_t, "timestamp_begin"); if (ret) { BT_LOGE_STR("Cannot add `timestamp_begin` field to event header field type."); goto end; } ret = bt_ctf_field_type_structure_add_field(packet_context_type, ts_begin_end_uint64_t, "timestamp_end"); if (ret) { BT_LOGE_STR("Cannot add `timestamp_end` field to event header field type."); goto end; } ret = bt_ctf_field_type_structure_add_field(packet_context_type, _uint64_t, "content_size"); if (ret) { BT_LOGE_STR("Cannot add `content_size` field to event header field type."); goto end; } ret = bt_ctf_field_type_structure_add_field(packet_context_type, _uint64_t, "packet_size"); if (ret) { BT_LOGE_STR("Cannot add `packet_size` field to event header field type."); goto end; } ret = bt_ctf_field_type_structure_add_field(packet_context_type, _uint64_t, "events_discarded"); if (ret) { BT_LOGE_STR("Cannot add `events_discarded` field to event header field type."); goto end; } bt_ctf_object_put_ref(stream_class->common.packet_context_field_type); stream_class->common.packet_context_field_type = (void *) packet_context_type; packet_context_type = NULL; end: if (ret) { bt_ctf_object_put_ref(packet_context_type); goto end; } bt_ctf_object_put_ref(_uint64_t); bt_ctf_object_put_ref(ts_begin_end_uint64_t); return ret; } static void bt_ctf_stream_class_destroy(struct bt_ctf_object *obj) { struct bt_ctf_stream_class *stream_class; stream_class = (void *) obj; BT_LOGD("Destroying CTF writer stream class: addr=%p, name=\"%s\", id=%" PRId64, stream_class, bt_ctf_stream_class_get_name(stream_class), bt_ctf_stream_class_get_id(stream_class)); bt_ctf_stream_class_common_finalize(BT_CTF_TO_COMMON(stream_class)); bt_ctf_object_put_ref(stream_class->clock); g_free(stream_class); } struct bt_ctf_stream_class *bt_ctf_stream_class_create(const char *name) { struct bt_ctf_stream_class *stream_class; int ret; BT_LOGD("Creating CTF writer stream class object: name=\"%s\"", name); stream_class = g_new0(struct bt_ctf_stream_class, 1); if (!stream_class) { BT_LOGE_STR("Failed to allocate one CTF writer stream class."); goto error; } ret = bt_ctf_stream_class_common_initialize(BT_CTF_TO_COMMON(stream_class), name, bt_ctf_stream_class_destroy); if (ret) { /* bt_ctf_stream_class_common_initialize() logs errors */ goto error; } ret = init_event_header(stream_class); if (ret) { BT_LOGE_STR("Cannot initialize stream class's event header field type."); goto error; } ret = init_packet_context(stream_class); if (ret) { BT_LOGE_STR("Cannot initialize stream class's packet context field type."); goto error; } BT_LOGD("Created CTF writer stream class object: addr=%p, name=\"%s\"", stream_class, name); return stream_class; error: BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_class); return stream_class; } static int try_map_clock_class(struct bt_ctf_stream_class *stream_class, struct bt_ctf_field_type *parent_ft, const char *field_name) { struct bt_ctf_clock_class *mapped_clock_class = NULL; int ret = 0; struct bt_ctf_field_type *ft = bt_ctf_field_type_structure_get_field_type_by_name(parent_ft, field_name); BT_ASSERT_DBG(stream_class->clock); if (!ft) { /* Field does not exist: not an error */ goto end; } BT_ASSERT_DBG(((struct bt_ctf_field_type_common *) ft)->id == BT_CTF_FIELD_TYPE_ID_INTEGER); mapped_clock_class = bt_ctf_field_type_integer_get_mapped_clock_class(ft); if (!mapped_clock_class) { struct bt_ctf_field_type *ft_copy; if (!stream_class->clock) { BT_LOGW("Cannot automatically set field's type mapped clock class: stream class's clock is not set: " "stream-class-addr=%p, stream-class-name=\"%s\", " "stream-class-id=%" PRId64 ", ft-addr=%p", stream_class, bt_ctf_stream_class_get_name(stream_class), bt_ctf_stream_class_get_id(stream_class), ft); ret = -1; goto end; } ft_copy = bt_ctf_field_type_copy(ft); if (!ft_copy) { BT_LOGE("Failed to copy integer field type: ft-addr=%p", ft); ret = -1; goto end; } ret = bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen( (void *) ft_copy, stream_class->clock->clock_class); BT_ASSERT_DBG(ret == 0); ret = bt_ctf_field_type_common_structure_replace_field( (void *) parent_ft, field_name, (void *) ft_copy); bt_ctf_object_put_ref(ft_copy); BT_LOGT("Automatically mapped field type to stream class's clock class: " "stream-class-addr=%p, stream-class-name=\"%s\", " "stream-class-id=%" PRId64 ", ft-addr=%p, " "ft-copy-addr=%p", stream_class, bt_ctf_stream_class_get_name(stream_class), bt_ctf_stream_class_get_id(stream_class), ft, ft_copy); } end: bt_ctf_object_put_ref(ft); bt_ctf_object_put_ref(mapped_clock_class); return ret; } BT_HIDDEN int bt_ctf_stream_class_map_clock_class( struct bt_ctf_stream_class *stream_class, struct bt_ctf_field_type *packet_context_type, struct bt_ctf_field_type *event_header_type) { int ret = 0; BT_ASSERT_DBG(stream_class); if (!stream_class->clock) { /* No clock class to map to */ goto end; } if (packet_context_type) { if (try_map_clock_class(stream_class, packet_context_type, "timestamp_begin")) { BT_LOGE_STR("Cannot automatically set stream class's packet context field type's `timestamp_begin` field's mapped clock class."); ret = -1; goto end; } if (try_map_clock_class(stream_class, packet_context_type, "timestamp_end")) { BT_LOGE_STR("Cannot automatically set stream class's packet context field type's `timestamp_end` field's mapped clock class."); ret = -1; goto end; } } if (event_header_type) { if (try_map_clock_class(stream_class, event_header_type, "timestamp")) { BT_LOGE_STR("Cannot automatically set stream class's event header field type's `timestamp` field's mapped clock class."); ret = -1; goto end; } } end: return ret; } struct bt_ctf_clock *bt_ctf_stream_class_get_clock( struct bt_ctf_stream_class *stream_class) { struct bt_ctf_clock *clock = NULL; if (!stream_class) { BT_LOGW_STR("Invalid parameter: stream class is NULL."); goto end; } if (!stream_class->clock) { BT_LOGT("Stream class has no clock: " "addr=%p, name=\"%s\", id=%" PRId64, stream_class, bt_ctf_stream_class_get_name(stream_class), bt_ctf_stream_class_get_id(stream_class)); goto end; } clock = bt_ctf_object_get_ref(stream_class->clock); end: return clock; } int bt_ctf_stream_class_set_clock( struct bt_ctf_stream_class *stream_class, struct bt_ctf_clock *clock) { int ret = 0; if (!stream_class || !clock) { BT_LOGW("Invalid parameter: stream class or clock is NULL: " "stream-class-addr=%p, clock-addr=%p", stream_class, clock); ret = -1; goto end; } if (stream_class->common.frozen) { BT_LOGW("Invalid parameter: stream class is frozen: " "addr=%p, name=\"%s\", id=%" PRId64, stream_class, bt_ctf_stream_class_get_name(stream_class), bt_ctf_stream_class_get_id(stream_class)); ret = -1; goto end; } /* Replace the current clock of this stream class. */ bt_ctf_object_put_ref(stream_class->clock); stream_class->clock = bt_ctf_object_get_ref(clock); BT_LOGT("Set stream class's clock: " "addr=%p, name=\"%s\", id=%" PRId64 ", " "clock-addr=%p, clock-name=\"%s\"", stream_class, bt_ctf_stream_class_get_name(stream_class), bt_ctf_stream_class_get_id(stream_class), stream_class->clock, bt_ctf_clock_get_name(stream_class->clock)); end: return ret; } BT_HIDDEN int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class, struct metadata_context *context) { int ret = 0; size_t i; struct bt_ctf_trace *trace; struct bt_ctf_field_type *packet_header_type = NULL; BT_LOGD("Serializing stream class's metadata: " "stream-class-addr=%p, stream-class-name=\"%s\", " "stream-class-id=%" PRId64 ", metadata-context-addr=%p", stream_class, bt_ctf_stream_class_get_name(stream_class), bt_ctf_stream_class_get_id(stream_class), context); g_string_assign(context->field_name, ""); context->current_indentation_level = 1; if (!stream_class->common.id_set) { BT_LOGW_STR("Stream class's ID is not set."); ret = -1; goto end; } g_string_append(context->string, "stream {\n"); /* * The reference to the trace is only borrowed since the * serialization of the stream class might have been triggered * by the trace's destruction. In such a case, the trace's * reference count would, unexepectedly, go through the sequence * 1 -> 0 -> 1 -> 0 -> ..., provoking an endless loop of destruction * and serialization. */ trace = BT_CTF_FROM_COMMON(bt_ctf_stream_class_common_borrow_trace( BT_CTF_TO_COMMON(stream_class))); BT_ASSERT_DBG(trace); packet_header_type = bt_ctf_trace_get_packet_header_field_type(trace); trace = NULL; if (packet_header_type) { struct bt_ctf_field_type *stream_id_type; stream_id_type = bt_ctf_field_type_structure_get_field_type_by_name( packet_header_type, "stream_id"); if (stream_id_type) { /* * Only set the stream's id if the trace's packet header * contains a stream_id field. This field is only * needed if the trace contains only one stream * class. */ g_string_append_printf(context->string, "\tid = %" PRId64 ";\n", stream_class->common.id); } bt_ctf_object_put_ref(stream_id_type); } if (stream_class->common.event_header_field_type) { BT_LOGD_STR("Serializing stream class's event header field type's metadata."); g_string_append(context->string, "\tevent.header := "); ret = bt_ctf_field_type_serialize_recursive( (void *) stream_class->common.event_header_field_type, context); if (ret) { BT_LOGW("Cannot serialize stream class's event header field type's metadata: " "ret=%d", ret); goto end; } g_string_append(context->string, ";"); } if (stream_class->common.packet_context_field_type) { BT_LOGD_STR("Serializing stream class's packet context field type's metadata."); g_string_append(context->string, "\n\n\tpacket.context := "); ret = bt_ctf_field_type_serialize_recursive( (void *) stream_class->common.packet_context_field_type, context); if (ret) { BT_LOGW("Cannot serialize stream class's packet context field type's metadata: " "ret=%d", ret); goto end; } g_string_append(context->string, ";"); } if (stream_class->common.event_context_field_type) { BT_LOGD_STR("Serializing stream class's event context field type's metadata."); g_string_append(context->string, "\n\n\tevent.context := "); ret = bt_ctf_field_type_serialize_recursive( (void *) stream_class->common.event_context_field_type, context); if (ret) { BT_LOGW("Cannot serialize stream class's event context field type's metadata: " "ret=%d", ret); goto end; } g_string_append(context->string, ";"); } g_string_append(context->string, "\n};\n\n"); for (i = 0; i < stream_class->common.event_classes->len; i++) { struct bt_ctf_event_class *event_class = stream_class->common.event_classes->pdata[i]; ret = bt_ctf_event_class_serialize(event_class, context); if (ret) { BT_LOGW("Cannot serialize event class's metadata: " "event-class-addr=%p, event-class-name=\"%s\", " "event-class-id=%" PRId64, event_class, bt_ctf_event_class_get_name(event_class), bt_ctf_event_class_get_id(event_class)); goto end; } } end: bt_ctf_object_put_ref(packet_header_type); context->current_indentation_level = 0; return ret; } struct bt_ctf_trace *bt_ctf_stream_class_get_trace( struct bt_ctf_stream_class *stream_class) { return bt_ctf_object_get_ref(bt_ctf_stream_class_common_borrow_trace( BT_CTF_TO_COMMON(stream_class))); } const char *bt_ctf_stream_class_get_name( struct bt_ctf_stream_class *stream_class) { return bt_ctf_stream_class_common_get_name(BT_CTF_TO_COMMON(stream_class)); } int bt_ctf_stream_class_set_name( struct bt_ctf_stream_class *stream_class, const char *name) { return bt_ctf_stream_class_common_set_name(BT_CTF_TO_COMMON(stream_class), name); } int64_t bt_ctf_stream_class_get_id( struct bt_ctf_stream_class *stream_class) { return bt_ctf_stream_class_common_get_id(BT_CTF_TO_COMMON(stream_class)); } int bt_ctf_stream_class_set_id( struct bt_ctf_stream_class *stream_class, uint64_t id) { return bt_ctf_stream_class_common_set_id(BT_CTF_TO_COMMON(stream_class), id); } struct bt_ctf_field_type *bt_ctf_stream_class_get_packet_context_type( struct bt_ctf_stream_class *stream_class) { return bt_ctf_object_get_ref( bt_ctf_stream_class_common_borrow_packet_context_field_type( BT_CTF_TO_COMMON(stream_class))); } int bt_ctf_stream_class_set_packet_context_type( struct bt_ctf_stream_class *stream_class, struct bt_ctf_field_type *packet_context_type) { return bt_ctf_stream_class_common_set_packet_context_field_type( BT_CTF_TO_COMMON(stream_class), (void *) packet_context_type); } struct bt_ctf_field_type * bt_ctf_stream_class_get_event_header_type( struct bt_ctf_stream_class *stream_class) { return bt_ctf_object_get_ref( bt_ctf_stream_class_common_borrow_event_header_field_type( BT_CTF_TO_COMMON(stream_class))); } int bt_ctf_stream_class_set_event_header_type( struct bt_ctf_stream_class *stream_class, struct bt_ctf_field_type *event_header_type) { return bt_ctf_stream_class_common_set_event_header_field_type( BT_CTF_TO_COMMON(stream_class), (void *) event_header_type); } struct bt_ctf_field_type * bt_ctf_stream_class_get_event_context_type( struct bt_ctf_stream_class *stream_class) { return bt_ctf_object_get_ref( bt_ctf_stream_class_common_borrow_event_context_field_type( BT_CTF_TO_COMMON(stream_class))); } int bt_ctf_stream_class_set_event_context_type( struct bt_ctf_stream_class *stream_class, struct bt_ctf_field_type *event_context_type) { return bt_ctf_stream_class_common_set_event_context_field_type( BT_CTF_TO_COMMON(stream_class), (void *) event_context_type); } int64_t bt_ctf_stream_class_get_event_class_count( struct bt_ctf_stream_class *stream_class) { return bt_ctf_stream_class_common_get_event_class_count( BT_CTF_TO_COMMON(stream_class)); } struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_index( struct bt_ctf_stream_class *stream_class, uint64_t index) { return bt_ctf_object_get_ref( bt_ctf_stream_class_common_borrow_event_class_by_index( BT_CTF_TO_COMMON(stream_class), index)); } struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_id( struct bt_ctf_stream_class *stream_class, uint64_t id) { return bt_ctf_object_get_ref( bt_ctf_stream_class_common_borrow_event_class_by_id( BT_CTF_TO_COMMON(stream_class), id)); } int bt_ctf_stream_class_add_event_class( struct bt_ctf_stream_class *stream_class, struct bt_ctf_event_class *event_class) { return bt_ctf_stream_class_common_add_event_class( BT_CTF_TO_COMMON(stream_class), BT_CTF_TO_COMMON(event_class), (bt_ctf_validation_flag_copy_field_type_func) bt_ctf_field_type_copy); }