Add field value is_set and reset functions to the public API
[babeltrace.git] / lib / ctf-ir / stream.c
index 402597edd3e1e1905e66c910ca8e1a87e76a934b..5741a7a520f6fbc4126f50ec031b5be12f269174 100644 (file)
@@ -48,6 +48,7 @@
 #include <babeltrace/compiler-internal.h>
 #include <babeltrace/align-internal.h>
 #include <inttypes.h>
+#include <unistd.h>
 
 static
 void bt_ctf_stream_destroy(struct bt_object *obj);
@@ -353,18 +354,50 @@ int set_packet_context_events_discarded(struct bt_ctf_stream *stream)
                goto end;
        }
 
-       ret = bt_ctf_field_unsigned_integer_set_value(field,
-               stream->discarded_events);
-       if (ret) {
-               BT_LOGW("Cannot set packet context field's `events_discarded` integer field's value: "
-                       "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64,
-                       stream, bt_ctf_stream_get_name(stream),
-                       field, stream->discarded_events);
+       /*
+        * If the field is set by the user, make sure that the value is
+        * greater than or equal to the stream's current count of
+        * discarded events. We do not allow wrapping here. If it's
+        * valid, update the stream's current count.
+        */
+       if (bt_ctf_field_value_is_set(field)) {
+               uint64_t user_val;
+
+               ret = bt_ctf_field_unsigned_integer_get_value(field,
+                       &user_val);
+               if (ret) {
+                       BT_LOGW("Cannot get packet context `events_discarded` field's unsigned value: "
+                               "stream-addr=%p, stream-name=\"%s\", field-addr=%p",
+                               stream, bt_ctf_stream_get_name(stream), field);
+                       goto end;
+               }
+
+               if (user_val < stream->discarded_events) {
+                       BT_LOGW("Invalid packet context `events_discarded` field's unsigned value: "
+                               "value is lesser than the stream's current discarded events count: "
+                               "stream-addr=%p, stream-name=\"%s\", field-addr=%p, "
+                               "value=%" PRIu64 ", "
+                               "stream-discarded-events-count=%" PRIu64,
+                               stream, bt_ctf_stream_get_name(stream), field,
+                               user_val, stream->discarded_events);
+                       goto end;
+               }
+
+               stream->discarded_events = user_val;
        } else {
-               BT_LOGV("Set packet context field's `events_discarded` field's value: "
-                       "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64,
-                       stream, bt_ctf_stream_get_name(stream),
-                       field, stream->discarded_events);
+               ret = bt_ctf_field_unsigned_integer_set_value(field,
+                       stream->discarded_events);
+               if (ret) {
+                       BT_LOGW("Cannot set packet context field's `events_discarded` integer field's value: "
+                               "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64,
+                               stream, bt_ctf_stream_get_name(stream),
+                               field, stream->discarded_events);
+               } else {
+                       BT_LOGV("Set packet context field's `events_discarded` field's value: "
+                               "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64,
+                               stream, bt_ctf_stream_get_name(stream),
+                               field, stream->discarded_events);
+               }
        }
 
 end:
@@ -634,46 +667,80 @@ int create_stream_file(struct bt_ctf_writer *writer,
                struct bt_ctf_stream *stream)
 {
        int fd;
-       GString *filename = g_string_new(stream->stream_class->name->str);
+       GString *filename = g_string_new(NULL);
+       int64_t stream_class_id;
+       char *file_path = NULL;
 
        BT_LOGD("Creating stream file: writer-addr=%p, stream-addr=%p, "
                "stream-name=\"%s\", stream-class-addr=%p, stream-class-name=\"%s\"",
                writer, stream, bt_ctf_stream_get_name(stream),
                stream->stream_class, stream->stream_class->name->str);
 
-       if (stream->stream_class->name->len == 0) {
-               int64_t ret;
-
-               ret = bt_ctf_stream_class_get_id(stream->stream_class);
-               if (ret < 0) {
-                       BT_LOGW("Cannot get stream class's ID: "
-                               "stream-class-addr=%p, "
-                               "stream-class-name=\"%s\"",
-                               stream->stream_class,
-                               stream->stream_class->name->str);
-                       fd = -1;
-                       goto end;
+       if (stream->name && stream->name->len > 0) {
+               /* Use stream name's base name as prefix */
+               gchar *basename = g_path_get_basename(stream->name->str);
+
+               assert(basename);
+
+               if (strcmp(basename, G_DIR_SEPARATOR_S) == 0) {
+                       g_string_assign(filename, "stream");
+               } else {
+                       g_string_assign(filename, basename);
+               }
+
+               g_free(basename);
+               goto append_ids;
+       }
+
+       if (stream->stream_class->name &&
+                       stream->stream_class->name->len > 0) {
+               /* Use stream class name's base name as prefix */
+               gchar *basename =
+                       g_path_get_basename(stream->stream_class->name->str);
+
+               assert(basename);
+
+               if (strcmp(basename, G_DIR_SEPARATOR_S) == 0) {
+                       g_string_assign(filename, "stream");
+               } else {
+                       g_string_assign(filename, basename);
                }
 
-               g_string_printf(filename, "stream_%" PRId64, ret);
+               g_free(basename);
+               goto append_ids;
+       }
+
+       /* Default to using `stream-` as prefix */
+       g_string_assign(filename, "stream");
+
+append_ids:
+       stream_class_id = bt_ctf_stream_class_get_id(stream->stream_class);
+       assert(stream_class_id >= 0);
+       assert(stream->id >= 0);
+       g_string_append_printf(filename, "-%" PRId64 "-%" PRId64,
+               stream_class_id, stream->id);
+
+       file_path = g_build_filename(writer->path->str, filename->str, NULL);
+       if (file_path == NULL) {
+               fd = -1;
+               goto end;
        }
 
-       g_string_append_printf(filename, "_%" PRIu32, stream->id);
-       fd = openat(writer->trace_dir_fd, filename->str,
+       fd = open(file_path,
                O_RDWR | O_CREAT | O_TRUNC,
                S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+       g_free(file_path);
        if (fd < 0) {
-               BT_LOGW("Failed to open stream file for writing: %s: "
-                       "writer-trace-dir-fd=%d, filename=\"%s\", "
-                       "ret=%d, errno=%d", strerror(errno),
-                       writer->trace_dir_fd, filename->str, fd, errno);
+               BT_LOGW_ERRNO("Failed to open stream file for writing",
+                       ": file_path=\"%s\", filename=\"%s\", ret=%d",
+                       file_path, filename->str, fd);
                goto end;
        }
 
        BT_LOGD("Created stream file for writing: "
-               "stream-addr=%p, stream-name=\"%s\", writer-trace-dir-fd=%d, "
+               "stream-addr=%p, stream-name=\"%s\", "
                "filename=\"%s\", fd=%d", stream, bt_ctf_stream_get_name(stream),
-               writer->trace_dir_fd, filename->str, fd);
+               filename->str, fd);
 
 end:
        g_string_free(filename, TRUE);
@@ -697,9 +764,10 @@ void component_destroy_listener(struct bt_component *component, void *data)
        g_hash_table_remove(stream->comp_cur_port, component);
 }
 
-struct bt_ctf_stream *bt_ctf_stream_create(
+static
+struct bt_ctf_stream *bt_ctf_stream_create_with_id_no_check(
                struct bt_ctf_stream_class *stream_class,
-               const char *name)
+               const char *name, uint64_t id)
 {
        int ret;
        struct bt_ctf_stream *stream = NULL;
@@ -712,10 +780,11 @@ struct bt_ctf_stream *bt_ctf_stream_create(
        }
 
        BT_LOGD("Creating stream object: stream-class-addr=%p, "
-               "stream-class-name=\"%s\", stream-name=\"%s\"",
+               "stream-class-name=\"%s\", stream-name=\"%s\", "
+               "stream-id=%" PRIu64,
                stream_class, bt_ctf_stream_class_get_name(stream_class),
-               name);
-       trace = bt_ctf_stream_class_get_trace(stream_class);
+               name, id);
+       trace = bt_ctf_stream_class_borrow_trace(stream_class);
        if (!trace) {
                BT_LOGW("Invalid parameter: cannot create stream from a stream class which is not part of trace: "
                        "stream-class-addr=%p, stream-class-name=\"%s\", "
@@ -739,6 +808,29 @@ struct bt_ctf_stream *bt_ctf_stream_create(
                goto error;
        }
 
+       if (id != -1ULL) {
+               /*
+                * Validate that the given ID is unique amongst all the
+                * existing trace's streams created from the same stream
+                * class.
+                */
+               size_t i;
+
+               for (i = 0; i < trace->streams->len; i++) {
+                       struct bt_ctf_stream *trace_stream =
+                               g_ptr_array_index(trace->streams, i);
+
+                       if (trace_stream->stream_class != stream_class) {
+                               continue;
+                       }
+
+                       if (trace_stream->id == id) {
+                               BT_LOGW_STR("Invalid parameter: another stream in the same trace already has this ID.");
+                               goto error;
+                       }
+               }
+       }
+
        stream = g_new0(struct bt_ctf_stream, 1);
        if (!stream) {
                BT_LOGE_STR("Failed to allocate one stream.");
@@ -751,9 +843,9 @@ struct bt_ctf_stream *bt_ctf_stream_create(
         * reachable; it needs its parent to remain valid.
         */
        bt_object_set_parent(stream, trace);
-       stream->id = stream_class->next_stream_id++;
        stream->stream_class = stream_class;
        stream->pos.fd = -1;
+       stream->id = (int64_t) id;
 
        stream->destroy_listeners = g_array_new(FALSE, TRUE,
                sizeof(struct bt_ctf_stream_destroy_listener));
@@ -774,8 +866,8 @@ struct bt_ctf_stream *bt_ctf_stream_create(
 
        if (trace->is_created_by_writer) {
                int fd;
-               writer = (struct bt_ctf_writer *)
-                       bt_object_get_parent(trace);
+               writer = (struct bt_ctf_writer *) bt_object_get_parent(trace);
+               stream->id = (int64_t) stream_class->next_stream_id++;
 
                BT_LOGD("Stream object belongs to a writer's trace: "
                        "writer-addr=%p", writer);
@@ -794,7 +886,7 @@ struct bt_ctf_stream *bt_ctf_stream_create(
                        /* Initialize events_discarded */
                        ret = try_set_structure_field_integer(
                                stream->packet_context, "events_discarded", 0);
-                       if (ret != 1) {
+                       if (ret < 0) {
                                BT_LOGW("Cannot set `events_discarded` field in packet context: "
                                        "ret=%d, packet-context-field-addr=%p",
                                        ret, stream->packet_context);
@@ -859,18 +951,71 @@ struct bt_ctf_stream *bt_ctf_stream_create(
 
        /* Add this stream to the trace's streams */
        g_ptr_array_add(trace->streams, stream);
-
-       BT_PUT(trace);
-       BT_PUT(writer);
        BT_LOGD("Created stream object: addr=%p", stream);
-       return stream;
+       goto end;
+
 error:
        BT_PUT(stream);
-       BT_PUT(trace);
-       BT_PUT(writer);
+
+end:
+       bt_put(writer);
+       return stream;
+}
+
+struct bt_ctf_stream *bt_ctf_stream_create_with_id(
+               struct bt_ctf_stream_class *stream_class,
+               const char *name, uint64_t id_param)
+{
+       struct bt_ctf_trace *trace;
+       struct bt_ctf_stream *stream = NULL;
+       int64_t id = (int64_t) id_param;
+
+       if (!stream_class) {
+               BT_LOGW_STR("Invalid parameter: stream class is NULL.");
+               goto end;
+       }
+
+       if (id < 0) {
+               BT_LOGW("Invalid parameter: invalid stream's ID: "
+                       "name=\"%s\", id=%" PRIu64,
+                       name, id_param);
+               goto end;
+       }
+
+       trace = bt_ctf_stream_class_borrow_trace(stream_class);
+       if (!trace) {
+               BT_LOGW("Invalid parameter: cannot create stream from a stream class which is not part of trace: "
+                       "stream-class-addr=%p, stream-class-name=\"%s\", "
+                       "stream-name=\"%s\", stream-id=%" PRIu64,
+                       stream_class, bt_ctf_stream_class_get_name(stream_class),
+                       name, id_param);
+               goto end;
+       }
+
+       if (trace->is_created_by_writer) {
+               BT_LOGW("Invalid parameter: cannot create a CTF writer stream with this function; use bt_ctf_stream_create(): "
+                       "stream-class-addr=%p, stream-class-name=\"%s\", "
+                       "stream-name=\"%s\", stream-id=%" PRIu64,
+                       stream_class, bt_ctf_stream_class_get_name(stream_class),
+                       name, id_param);
+               goto end;
+       }
+
+       stream = bt_ctf_stream_create_with_id_no_check(stream_class,
+               name, id_param);
+
+end:
        return stream;
 }
 
+struct bt_ctf_stream *bt_ctf_stream_create(
+               struct bt_ctf_stream_class *stream_class,
+               const char *name)
+{
+       return bt_ctf_stream_create_with_id_no_check(stream_class,
+               name, -1ULL);
+}
+
 struct bt_ctf_stream_class *bt_ctf_stream_get_class(
                struct bt_ctf_stream *stream)
 {
@@ -887,20 +1032,20 @@ end:
        return stream_class;
 }
 
-int64_t bt_ctf_stream_get_discarded_events_count(
+int bt_ctf_stream_get_discarded_events_count(
                struct bt_ctf_stream *stream, uint64_t *count)
 {
-       int64_t ret = 0;
+       int ret = 0;
 
        if (!stream) {
                BT_LOGW_STR("Invalid parameter: stream is NULL.");
-               ret = (int64_t) -1;
+               ret = -1;
                goto end;
        }
 
        if (!count) {
                BT_LOGW_STR("Invalid parameter: count is NULL.");
-               ret = (int64_t) -1;
+               ret = -1;
                goto end;
        }
 
@@ -908,7 +1053,7 @@ int64_t bt_ctf_stream_get_discarded_events_count(
                BT_LOGW("Invalid parameter: stream is not a CTF writer stream: "
                        "stream-addr=%p, stream-name=\"%s\"",
                        stream, bt_ctf_stream_get_name(stream));
-               ret = (int64_t) -1;
+               ret = -1;
                goto end;
        }
 
@@ -1011,10 +1156,14 @@ static int auto_populate_event_header(struct bt_ctf_stream *stream,
        int ret = 0;
        struct bt_ctf_field *id_field = NULL, *timestamp_field = NULL;
        struct bt_ctf_clock_class *mapped_clock_class = NULL;
-       uint64_t event_class_id;
+       int64_t event_class_id;
 
        assert(event);
 
+       if (!event->event_header) {
+               goto end;
+       }
+
        if (event->frozen) {
                BT_LOGW_STR("Cannot populate event header field: event is frozen.");
                ret = -1;
@@ -1026,8 +1175,12 @@ static int auto_populate_event_header(struct bt_ctf_stream *stream,
                stream, bt_ctf_stream_get_name(stream), event);
 
        id_field = bt_ctf_field_structure_get_field(event->event_header, "id");
-       event_class_id = (uint64_t) bt_ctf_event_class_get_id(event->event_class);
-       assert(event_class_id >= 0);
+       event_class_id = bt_ctf_event_class_get_id(event->event_class);
+       if (event_class_id < 0) {
+               BT_LOGE("Event class ID cannot be found");
+               ret = -1;
+               goto end;
+       }
        if (id_field && bt_ctf_field_type_is_integer(id_field->type)) {
                ret = set_integer_field_value(id_field, event_class_id);
                if (ret) {
@@ -1340,9 +1493,10 @@ void reset_structure_field(struct bt_ctf_field *structure, const char *name)
        struct bt_ctf_field *member;
 
        member = bt_ctf_field_structure_get_field(structure, name);
-       assert(member);
-       (void) bt_ctf_field_reset(member);
-       bt_put(member);
+       if (member) {
+               (void) bt_ctf_field_reset_value(member);
+               bt_put(member);
+       }
 }
 
 int bt_ctf_stream_flush(struct bt_ctf_stream *stream)
@@ -1352,11 +1506,12 @@ int bt_ctf_stream_flush(struct bt_ctf_stream *stream)
        struct bt_ctf_stream_pos packet_context_pos;
        struct bt_ctf_trace *trace;
        enum bt_ctf_byte_order native_byte_order;
+       bool has_packet_size = false;
 
        if (!stream) {
                BT_LOGW_STR("Invalid parameter: stream is NULL.");
                ret = -1;
-               goto end;
+               goto end_no_stream;
        }
 
        if (stream->pos.fd < 0) {
@@ -1365,19 +1520,23 @@ int bt_ctf_stream_flush(struct bt_ctf_stream *stream)
                goto end;
        }
 
-       if (stream->flushed_packet_count == 1) {
+       if (stream->packet_context) {
                struct bt_ctf_field *packet_size_field;
 
+               packet_size_field = bt_ctf_field_structure_get_field(
+                               stream->packet_context, "packet_size");
+               has_packet_size = (packet_size_field != NULL);
+               bt_put(packet_size_field);
+       }
+
+       if (stream->flushed_packet_count == 1) {
                if (!stream->packet_context) {
                        BT_LOGW_STR("Cannot flush a stream which has no packet context field more than once.");
                        ret = -1;
                        goto end;
                }
 
-               packet_size_field = bt_ctf_field_structure_get_field(
-                       stream->packet_context, "packet_size");
-               bt_put(packet_size_field);
-               if (!packet_size_field) {
+               if (!has_packet_size) {
                        BT_LOGW_STR("Cannot flush a stream which has no packet context's `packet_size` field more than once.");
                        ret = -1;
                        goto end;
@@ -1452,13 +1611,15 @@ int bt_ctf_stream_flush(struct bt_ctf_stream *stream)
                        stream->pos.offset, stream->pos.packet_size);
 
                /* Write event header */
-               BT_LOGV_STR("Serializing event's header field.");
-               ret = bt_ctf_field_serialize(event->event_header,
-                       &stream->pos, native_byte_order);
-               if (ret) {
-                       BT_LOGW("Cannot serialize event's header field: "
-                               "field-addr=%p", event->event_header);
-                       goto end;
+               if (event->event_header) {
+                       BT_LOGV_STR("Serializing event's header field.");
+                       ret = bt_ctf_field_serialize(event->event_header,
+                                       &stream->pos, native_byte_order);
+                       if (ret) {
+                               BT_LOGW("Cannot serialize event's header field: "
+                                               "field-addr=%p", event->event_header);
+                               goto end;
+                       }
                }
 
                /* Write stream event context */
@@ -1483,8 +1644,24 @@ int bt_ctf_stream_flush(struct bt_ctf_stream *stream)
                }
        }
 
+       if (!has_packet_size && stream->pos.offset % 8 != 0) {
+               BT_LOGW("Stream's packet context field type has no `packet_size` field, "
+                       "but current content size is not a multiple of 8 bits: "
+                       "content-size=%" PRId64 ", "
+                       "packet-size=%" PRIu64,
+                       stream->pos.offset,
+                       stream->pos.packet_size);
+               ret = -1;
+               goto end;
+       }
+
        assert(stream->pos.packet_size % 8 == 0);
 
+       /*
+        * Remove extra padding bytes.
+        */
+       stream->pos.packet_size = (stream->pos.offset + 7) & ~7;
+
        if (stream->packet_context) {
                /*
                 * The whole packet is serialized at this point. Make sure that,
@@ -1537,12 +1714,16 @@ int bt_ctf_stream_flush(struct bt_ctf_stream *stream)
        g_ptr_array_set_size(stream->events, 0);
        stream->flushed_packet_count++;
        stream->size += stream->pos.packet_size / CHAR_BIT;
+
 end:
        /* Reset automatically-set fields. */
-       reset_structure_field(stream->packet_context, "timestamp_begin");
-       reset_structure_field(stream->packet_context, "timestamp_end");
-       reset_structure_field(stream->packet_context, "packet_size");
-       reset_structure_field(stream->packet_context, "content_size");
+       if (stream->packet_context) {
+               reset_structure_field(stream->packet_context, "timestamp_begin");
+               reset_structure_field(stream->packet_context, "timestamp_end");
+               reset_structure_field(stream->packet_context, "packet_size");
+               reset_structure_field(stream->packet_context, "content_size");
+               reset_structure_field(stream->packet_context, "events_discarded");
+       }
 
        if (ret < 0) {
                /*
@@ -1557,6 +1738,8 @@ end:
                        "packet-size=%" PRIu64,
                        stream->pos.offset, stream->pos.packet_size);
        }
+
+end_no_stream:
        return ret;
 }
 
@@ -1606,16 +1789,14 @@ void bt_ctf_stream_destroy(struct bt_object *obj)
                        ret = ftruncate(stream->pos.fd, stream->size);
                } while (ret == -1 && errno == EINTR);
                if (ret) {
-                       BT_LOGE("Failed to truncate stream file: %s: "
-                               "ret=%d, errno=%d, size=%" PRIu64,
-                               strerror(errno), ret, errno,
-                               (uint64_t) stream->size);
+                       BT_LOGE_ERRNO("Failed to truncate stream file",
+                               ": ret=%d, size=%" PRIu64,
+                               ret, (uint64_t) stream->size);
                }
 
                if (close(stream->pos.fd)) {
-                       BT_LOGE("Failed to close stream file: %s: "
-                               "ret=%d, errno=%d", strerror(errno),
-                               ret, errno);
+                       BT_LOGE_ERRNO("Failed to close stream file",
+                               ": ret=%d", ret);
                }
        }
 
@@ -1681,7 +1862,7 @@ int _set_structure_field_integer(struct bt_ctf_field *structure, char *name,
        }
 
        /* Make sure the payload has not already been set. */
-       if (!force && bt_ctf_field_is_set(integer)) {
+       if (!force && bt_ctf_field_value_is_set(integer)) {
                /* Payload already set, not an error */
                BT_LOGV("Field's payload is already set: struct-field-addr=%p, "
                        "name=\"%s\", force=%d", structure, name, force);
@@ -1777,15 +1958,16 @@ void bt_ctf_stream_map_component_to_port(struct bt_ctf_stream *stream,
         * listener so that we remove this hash table entry when we know
         * the component is destroyed.
         */
-       bt_component_add_destroy_listener(comp, component_destroy_listener,
-               stream);
-       g_hash_table_insert(stream->comp_cur_port, comp, port);
-       BT_LOGV("Mapped component to port for stream: stream-addr=%p, "
-               "stream-name=\"%s\", comp-addr=%p, comp-name=\"%s\", "
-               "port-addr=%p, port-name=\"%s\"",
+       BT_LOGV("Adding component's destroy listener for stream: "
+               "stream-addr=%p, stream-name=\"%s\", comp-addr=%p, "
+               "comp-name=\"%s\", port-addr=%p, port-name=\"%s\"",
                stream, bt_ctf_stream_get_name(stream),
                comp, bt_component_get_name(comp), port,
                bt_port_get_name(port));
+       bt_component_add_destroy_listener(comp, component_destroy_listener,
+               stream);
+       g_hash_table_insert(stream->comp_cur_port, comp, port);
+       BT_LOGV_STR("Mapped component to port for stream.");
 }
 
 BT_HIDDEN
@@ -1838,3 +2020,23 @@ void bt_ctf_stream_remove_destroy_listener(struct bt_ctf_stream *stream,
                }
        }
 }
+
+int64_t bt_ctf_stream_get_id(struct bt_ctf_stream *stream)
+{
+       int64_t ret;
+
+       if (!stream) {
+               BT_LOGW_STR("Invalid parameter: stream is NULL.");
+               ret = (int64_t) -1;
+               goto end;
+       }
+
+       ret = stream->id;
+       if (ret < 0) {
+               BT_LOGV("Stream's ID is not set: addr=%p, name=\"%s\"",
+                       stream, bt_ctf_stream_get_name(stream));
+       }
+
+end:
+       return ret;
+}
This page took 0.031287 seconds and 4 git commands to generate.