Validate CTF semantics in selected CTF IR functions
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Thu, 18 May 2017 23:52:41 +0000 (19:52 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Sun, 28 May 2017 16:57:44 +0000 (12:57 -0400)
Before this patch, the semantics suggested by the CTF specification are
not validated in CTF IR itself. For example, the library does not
enforce that, if there's more than one stream class contained in a given
trace, its packet header field type's `stream_id` field must exist
(which also means that the packet header field type must be set).

When you add a stream class to a trace created by a CTF writer object
(implicit when you call bt_ctf_writer_create_stream(), for example), we
proceed to automatically map selected field types to the stream class's
clock if it's set. Those fields are:

* Packet context field type: `timestamp_begin`
* Packet context field type: `timestamp_end`
* Event header field type: `timestamp`

If those fields are not mapped to a clock class, and if there's no
current stream class's clock, then the whole
bt_ctf_trace_add_stream_class() function fails.

Those mappings are needed to make a valid CTF trace. The field types
cannot be mapped at stream class creation time because the user can
override the packet context and event header field types and still have
those fields without a mapping. To make this use case more easy in CTF
writer context, the mapping is always done automatically in
bt_ctf_trace_add_stream_class().

This has the side effect that this sequence of operations is not valid
anymore:

1. Create a trace.
2. Create a default stream class with bt_ctf_trace_add_stream_class().
3. Add the default stream class (2) to the created trace (1).

Because bt_ctf_trace_add_stream_class() creates an initial packet
context field type which contains the `timestamp_begin` and
`timestamp_end` fields, and because those fields are only valid at
bt_ctf_trace_add_stream_class() call time when they are mapped to a
clock class, and because we're not in CTF writer context, then they are
not automatically mapped. This is why, in non CTF writer mode (graph
mode), the user should prefer bt_ctf_stream_class_create_empty(), or
manually get the `timestamp_begin` and `timestamp_end` field types and
map them to a clock class which is also part of the trace in which to
add the stream class.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
include/babeltrace/ctf-ir/field-types-internal.h
include/babeltrace/ctf-ir/stream-class-internal.h
lib/ctf-ir/field-types.c
lib/ctf-ir/stream-class.c
lib/ctf-ir/trace.c
tests/lib/test_ctf_writer.c

index 9e05d4c243ed227da39680833b479dd07c5815e6..8de2e05d3711b583bdec767e653ad01c2658d2de 100644 (file)
@@ -215,6 +215,11 @@ BT_HIDDEN
 int bt_ctf_field_type_get_field_index(struct bt_ctf_field_type *type,
                const char *name);
 
+BT_HIDDEN
+int bt_ctf_field_type_integer_set_mapped_clock_class_no_check(
+               struct bt_ctf_field_type *int_field_type,
+               struct bt_ctf_clock_class *clock_class);
+
 static inline
 const char *bt_ctf_field_type_id_string(enum bt_ctf_field_type_id type_id)
 {
index 7e5dcb9b1bca71fbbea0fd0ea712be2e095a723e..a7c98266154852267bd0390a7e962a4f50b076a6 100644 (file)
@@ -81,6 +81,12 @@ BT_HIDDEN
 int bt_ctf_stream_class_set_id_no_check(
                struct bt_ctf_stream_class *stream_class, int64_t id);
 
+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);
+
 static inline
 struct bt_ctf_trace *bt_ctf_stream_class_borrow_trace(
                struct bt_ctf_stream_class *stream_class)
index 92211a49f750409619560ba9be3b0206c9f99ef5..0cf5d1ef4293c7a0f3732f4be49ee59346abfec8 100644 (file)
@@ -1181,7 +1181,8 @@ end:
        return clock_class;
 }
 
-int bt_ctf_field_type_integer_set_mapped_clock_class(
+BT_HIDDEN
+int bt_ctf_field_type_integer_set_mapped_clock_class_no_check(
                struct bt_ctf_field_type *type,
                struct bt_ctf_clock_class *clock_class)
 {
@@ -1200,10 +1201,10 @@ int bt_ctf_field_type_integer_set_mapped_clock_class(
                goto end;
        }
 
-       if (type->frozen) {
-               BT_LOGW("Invalid parameter: field type is frozen: addr=%p",
-                       type);
-               ret = -1;
+       if (type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
+               BT_LOGW("Invalid parameter: field type is not an integer field type: "
+                       "addr=%p, ft-id=%s", type,
+                       bt_ctf_field_type_id_string(type->id));
                goto end;
        }
 
@@ -1226,6 +1227,32 @@ end:
        return ret;
 }
 
+int bt_ctf_field_type_integer_set_mapped_clock_class(
+               struct bt_ctf_field_type *type,
+               struct bt_ctf_clock_class *clock_class)
+{
+       int ret = 0;
+
+       if (!type) {
+               BT_LOGW_STR("Invalid parameter: field type is NULL.");
+               ret = -1;
+               goto end;
+       }
+
+       if (type->frozen) {
+               BT_LOGW("Invalid parameter: field type is frozen: addr=%p",
+                       type);
+               ret = -1;
+               goto end;
+       }
+
+       ret = bt_ctf_field_type_integer_set_mapped_clock_class_no_check(
+               type, clock_class);
+
+end:
+       return ret;
+}
+
 static
 void bt_ctf_field_type_enum_iter_destroy(struct bt_object *obj)
 {
index ab2ec84ff1c25abff9c0295fd1c5e6a9ea51744a..9a63043acccdc2d01c34d3239124031ae35f24dd 100644 (file)
@@ -49,6 +49,7 @@
 #include <babeltrace/endian-internal.h>
 #include <inttypes.h>
 #include <stdint.h>
+#include <stdbool.h>
 
 static
 void bt_ctf_stream_class_destroy(struct bt_object *obj);
@@ -249,30 +250,6 @@ int bt_ctf_stream_class_set_clock(struct bt_ctf_stream_class *stream_class,
                goto end;
        }
 
-       /*
-        * Look for a "timestamp" integer field type in the stream
-        * class's event header field type and map the stream class's
-        * clock's class to that field type if there's no current
-        * mapping.
-        */
-       timestamp_field = bt_ctf_field_type_structure_get_field_type_by_name(
-               stream_class->event_header_type, "timestamp");
-       if (timestamp_field) {
-               struct bt_ctf_clock_class *mapped_clock_class =
-                       bt_ctf_field_type_integer_get_mapped_clock_class(
-                               timestamp_field);
-
-               if (!mapped_clock_class) {
-                       ret = bt_ctf_field_type_integer_set_mapped_clock_class(
-                               timestamp_field, clock->clock_class);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               BT_PUT(mapped_clock_class);
-       }
-
        /* Replace the current clock of this stream class. */
        bt_put(stream_class->clock);
        stream_class->clock = bt_get(clock);
@@ -465,6 +442,14 @@ int bt_ctf_stream_class_add_event_class(
        const enum bt_ctf_validation_flag validation_flags =
                BT_CTF_VALIDATION_FLAG_EVENT;
 
+       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, "
@@ -475,14 +460,6 @@ int bt_ctf_stream_class_add_event_class(
                bt_ctf_event_class_get_name(event_class),
                bt_ctf_event_class_get_id(event_class));
 
-       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;
-       }
-
        trace = bt_ctf_stream_class_get_trace(stream_class);
        if (trace && trace->is_static) {
                ret = -1;
@@ -1047,18 +1024,24 @@ int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class,
        }
 
        g_string_append_printf(context->string,
-               "stream {\n\tid = %" PRId64 ";\n\tevent.header := ",
-               stream_class->id);
-       ret = bt_ctf_field_type_serialize(stream_class->event_header_type,
-               context);
-       if (ret) {
-               BT_LOGW("Cannot serialize stream class's event header field type's metadata: "
-                       "ret=%d", ret);
-               goto end;
+               "stream {\n\tid = %" PRId64 ";\n", stream_class->id);
+       if (stream_class->event_header_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(stream_class->event_header_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->packet_context_type) {
-               g_string_append(context->string, ";\n\n\tpacket.context := ");
+               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(stream_class->packet_context_type,
                        context);
                if (ret) {
@@ -1066,10 +1049,12 @@ int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class,
                                "ret=%d", ret);
                        goto end;
                }
+               g_string_append(context->string, ";");
        }
 
        if (stream_class->event_context_type) {
-               g_string_append(context->string, ";\n\n\tevent.context := ");
+               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(
                        stream_class->event_context_type, context);
                if (ret) {
@@ -1077,9 +1062,11 @@ int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class,
                                "ret=%d", ret);
                        goto end;
                }
+               g_string_append(context->string, ";");
        }
 
-       g_string_append(context->string, ";\n};\n\n");
+       g_string_append(context->string, "\n};\n\n");
+
        for (i = 0; i < stream_class->event_classes->len; i++) {
                struct bt_ctf_event_class *event_class =
                        stream_class->event_classes->pdata[i];
@@ -1178,6 +1165,7 @@ int init_packet_context(struct bt_ctf_stream_class *stream_class)
                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;
 
        if (!packet_context_type) {
                BT_LOGE_STR("Cannot create empty structure field type.");
@@ -1185,19 +1173,26 @@ int init_packet_context(struct bt_ctf_stream_class *stream_class)
                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,
-               _uint64_t, "timestamp_begin");
+               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,
-               _uint64_t, "timestamp_end");
+               ts_begin_end_uint64_t, "timestamp_end");
        if (ret) {
                BT_LOGE_STR("Cannot add `timestamp_end` field to event header field type.");
                goto end;
@@ -1232,5 +1227,103 @@ end:
        }
 
        bt_put(_uint64_t);
+       bt_put(ts_begin_end_uint64_t);
+       return ret;
+}
+
+static
+int try_map_clock_class(struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_field_type *ft)
+{
+       struct bt_ctf_clock_class *mapped_clock_class = NULL;
+       int ret = 0;
+
+       if (!ft) {
+               /* Field does not exist: not an error */
+               goto end;
+       }
+
+       assert(bt_ctf_field_type_is_integer(ft));
+       mapped_clock_class =
+               bt_ctf_field_type_integer_get_mapped_clock_class(ft);
+       if (!mapped_clock_class) {
+               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;
+               }
+
+               ret = bt_ctf_field_type_integer_set_mapped_clock_class_no_check(
+                       ft, stream_class->clock->clock_class);
+               if (ret) {
+                       BT_LOGW("Cannot set field type's mapped clock class: "
+                               "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);
+                       goto end;
+               }
+
+               BT_LOGV("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",
+                       stream_class, bt_ctf_stream_class_get_name(stream_class),
+                       bt_ctf_stream_class_get_id(stream_class), ft);
+       }
+
+end:
+       bt_put(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)
+{
+       struct bt_ctf_field_type *ft = NULL;
+       int ret = 0;
+
+       assert(stream_class);
+
+       if (packet_context_type) {
+               ft = bt_ctf_field_type_structure_get_field_type_by_name(
+                       packet_context_type, "timestamp_begin");
+               if (try_map_clock_class(stream_class, ft)) {
+                       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;
+               }
+
+               bt_put(ft);
+               ft = bt_ctf_field_type_structure_get_field_type_by_name(
+                       packet_context_type, "timestamp_end");
+               if (try_map_clock_class(stream_class, ft)) {
+                       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;
+               }
+
+               BT_PUT(ft);
+       }
+
+       if (event_header_type) {
+               ft = bt_ctf_field_type_structure_get_field_type_by_name(
+                       event_header_type, "timestamp");
+               if (try_map_clock_class(stream_class, ft)) {
+                       BT_LOGE_STR("Cannot automatically set stream class's event header field type's `timestamp` field's mapped clock class.");
+                       ret = -1;
+                       goto end;
+               }
+
+               BT_PUT(ft);
+       }
+
+end:
        return ret;
 }
index d36d49816e147e11733093f00b50280f66a76fa7..47f136d5ddb7073a4dcc735a84d25a84ad44362c 100644 (file)
@@ -26,6 +26,9 @@
  * SOFTWARE.
  */
 
+#define BT_LOG_TAG "TRACE"
+#include <babeltrace/lib-logging-internal.h>
+
 #include <babeltrace/ctf-ir/trace-internal.h>
 #include <babeltrace/ctf-ir/clock-class-internal.h>
 #include <babeltrace/ctf-ir/stream-internal.h>
@@ -464,6 +467,452 @@ end:
        return clock_class;
 }
 
+static
+bool packet_header_field_type_is_valid(struct bt_ctf_trace *trace,
+               struct bt_ctf_field_type *packet_header_type)
+{
+       int ret;
+       bool is_valid = true;
+       struct bt_ctf_field_type *field_type = NULL;
+
+       if (!packet_header_type) {
+               /*
+                * No packet header field type: trace must have only
+                * one stream. At this point the stream class being
+                * added is not part of the trace yet, so we validate
+                * that the trace contains no stream classes yet.
+                */
+               if (trace->stream_classes->len >= 1) {
+                       BT_LOGW_STR("Invalid packet header field type: "
+                               "packet header field type does not exist but there's more than one stream class in the trace.");
+                       goto invalid;
+               }
+
+               /* No packet header field type: valid at this point */
+               goto end;
+       }
+
+       /* Packet header field type, if it exists, must be a structure */
+       if (!bt_ctf_field_type_is_structure(packet_header_type)) {
+               BT_LOGW("Invalid packet header field type: must be a structure field type if it exists: "
+                       "ft-addr=%p, ft-id=%s",
+                       packet_header_type,
+                       bt_ctf_field_type_id_string(packet_header_type->id));
+               goto invalid;
+       }
+
+       /*
+        * If there's a `magic` field, it must be a 32-bit unsigned
+        * integer field type. Also it must be the first field of the
+        * packet header field type.
+        */
+       field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_header_type, "magic");
+       if (field_type) {
+               const char *field_name;
+
+               if (!bt_ctf_field_type_is_integer(field_type)) {
+                       BT_LOGW("Invalid packet header field type: `magic` field must be an integer field type: "
+                               "magic-ft-addr=%p, magic-ft-id=%s",
+                               field_type,
+                               bt_ctf_field_type_id_string(field_type->id));
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_is_signed(field_type)) {
+                       BT_LOGW("Invalid packet header field type: `magic` field must be an unsigned integer field type: "
+                               "magic-ft-addr=%p", field_type);
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_get_size(field_type) != 32) {
+                       BT_LOGW("Invalid packet header field type: `magic` field must be a 32-bit unsigned integer field type: "
+                               "magic-ft-addr=%p, magic-ft-size=%u",
+                               field_type,
+                               bt_ctf_field_type_integer_get_size(field_type));
+                       goto invalid;
+               }
+
+               ret = bt_ctf_field_type_structure_get_field_by_index(
+                       packet_header_type, &field_name, NULL, 0);
+               assert(ret == 0);
+
+               if (strcmp(field_name, "magic") != 0) {
+                       BT_LOGW("Invalid packet header field type: `magic` field must be the first field: "
+                               "magic-ft-addr=%p, first-field-name=\"%s\"",
+                               field_type, field_name);
+                       goto invalid;
+               }
+
+               BT_PUT(field_type);
+       }
+
+       /*
+        * If there's a `uuid` field, it must be an array field type of
+        * length 16 with an 8-bit unsigned integer element field type.
+        */
+       field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_header_type, "uuid");
+       if (field_type) {
+               struct bt_ctf_field_type *elem_ft;
+
+               if (!bt_ctf_field_type_is_array(field_type)) {
+                       BT_LOGW("Invalid packet header field type: `uuid` field must be an array field type: "
+                               "uuid-ft-addr=%p, uuid-ft-id=%s",
+                               field_type,
+                               bt_ctf_field_type_id_string(field_type->id));
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_array_get_length(field_type) != 16) {
+                       BT_LOGW("Invalid packet header field type: `uuid` array field type's length must be 16: "
+                               "uuid-ft-addr=%p, uuid-ft-length=%" PRId64,
+                               field_type,
+                               bt_ctf_field_type_array_get_length(field_type));
+                       goto invalid;
+               }
+
+               elem_ft = bt_ctf_field_type_array_get_element_type(field_type);
+               assert(elem_ft);
+
+               if (!bt_ctf_field_type_is_integer(elem_ft)) {
+                       BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an integer field type: "
+                               "elem-ft-addr=%p, elem-ft-id=%s",
+                               elem_ft,
+                               bt_ctf_field_type_id_string(elem_ft->id));
+                       bt_put(elem_ft);
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_is_signed(elem_ft)) {
+                       BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an unsigned integer field type: "
+                               "elem-ft-addr=%p", elem_ft);
+                       bt_put(elem_ft);
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_get_size(elem_ft) != 8) {
+                       BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an 8-bit unsigned integer field type: "
+                               "elem-ft-addr=%p, elem-ft-size=%u",
+                               elem_ft,
+                               bt_ctf_field_type_integer_get_size(elem_ft));
+                       bt_put(elem_ft);
+                       goto invalid;
+               }
+
+               bt_put(elem_ft);
+               BT_PUT(field_type);
+       }
+
+       /*
+        * The `stream_id` field must exist if there's more than one
+        * stream classes in the trace.
+        */
+       field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_header_type, "stream_id");
+
+       if (!field_type && trace->stream_classes->len >= 1) {
+               BT_LOGW_STR("Invalid packet header field type: "
+                       "`stream_id` field does not exist but there's more than one stream class in the trace.");
+               goto invalid;
+       }
+
+       /*
+        * If there's a `stream_id` field, it must be an unsigned
+        * integer field type.
+        */
+       if (field_type) {
+               if (!bt_ctf_field_type_is_integer(field_type)) {
+                       BT_LOGW("Invalid packet header field type: `stream_id` field must be an integer field type: "
+                               "stream-id-ft-addr=%p, stream-id-ft-id=%s",
+                               field_type,
+                               bt_ctf_field_type_id_string(field_type->id));
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_is_signed(field_type)) {
+                       BT_LOGW("Invalid packet header field type: `stream_id` field must be an unsigned integer field type: "
+                               "stream-id-ft-addr=%p", field_type);
+                       goto invalid;
+               }
+
+               BT_PUT(field_type);
+       }
+
+       goto end;
+
+invalid:
+       is_valid = false;
+
+end:
+       bt_put(field_type);
+       return is_valid;
+}
+
+static
+bool packet_context_field_type_is_valid(struct bt_ctf_trace *trace,
+               struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_field_type *packet_context_type)
+{
+       bool is_valid = true;
+       struct bt_ctf_field_type *field_type = NULL;
+
+       if (!packet_context_type) {
+               /* No packet context field type: valid at this point */
+               goto end;
+       }
+
+       /* Packet context field type, if it exists, must be a structure */
+       if (!bt_ctf_field_type_is_structure(packet_context_type)) {
+               BT_LOGW("Invalid packet context field type: must be a structure field type if it exists: "
+                       "ft-addr=%p, ft-id=%s",
+                       packet_context_type,
+                       bt_ctf_field_type_id_string(packet_context_type->id));
+               goto invalid;
+       }
+
+       /*
+        * If there's a `packet_size` field, it must be an unsigned
+        * integer field type.
+        */
+       field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_context_type, "packet_size");
+       if (field_type) {
+               if (!bt_ctf_field_type_is_integer(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `packet_size` field must be an integer field type: "
+                               "packet-size-ft-addr=%p, packet-size-ft-id=%s",
+                               field_type,
+                               bt_ctf_field_type_id_string(field_type->id));
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_is_signed(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `packet_size` field must be an unsigned integer field type: "
+                               "packet-size-ft-addr=%p", field_type);
+                       goto invalid;
+               }
+
+               BT_PUT(field_type);
+       }
+
+       /*
+        * If there's a `content_size` field, it must be an unsigned
+        * integer field type.
+        */
+       field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_context_type, "content_size");
+       if (field_type) {
+               if (!bt_ctf_field_type_is_integer(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `content_size` field must be an integer field type: "
+                               "content-size-ft-addr=%p, content-size-ft-id=%s",
+                               field_type,
+                               bt_ctf_field_type_id_string(field_type->id));
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_is_signed(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `content_size` field must be an unsigned integer field type: "
+                               "content-size-ft-addr=%p", field_type);
+                       goto invalid;
+               }
+
+               BT_PUT(field_type);
+       }
+
+       /*
+        * If there's a `events_discarded` field, it must be an unsigned
+        * integer field type.
+        */
+       field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_context_type, "events_discarded");
+       if (field_type) {
+               if (!bt_ctf_field_type_is_integer(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `events_discarded` field must be an integer field type: "
+                               "events-discarded-ft-addr=%p, events-discarded-ft-id=%s",
+                               field_type,
+                               bt_ctf_field_type_id_string(field_type->id));
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_is_signed(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `events_discarded` field must be an unsigned integer field type: "
+                               "events-discarded-ft-addr=%p", field_type);
+                       goto invalid;
+               }
+
+               BT_PUT(field_type);
+       }
+
+       /*
+        * If there's a `timestamp_begin` field, it must be an unsigned
+        * integer field type. Also, if the trace is not a CTF writer's
+        * trace, then we cannot automatically set the mapped clock
+        * class of this field, so it must have a mapped clock class.
+        */
+       field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_context_type, "timestamp_begin");
+       if (field_type) {
+               if (!bt_ctf_field_type_is_integer(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be an integer field type: "
+                               "timestamp-begin-ft-addr=%p, timestamp-begin-ft-id=%s",
+                               field_type,
+                               bt_ctf_field_type_id_string(field_type->id));
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_is_signed(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be an unsigned integer field type: "
+                               "timestamp-begin-ft-addr=%p", field_type);
+                       goto invalid;
+               }
+
+               if (!trace->is_created_by_writer) {
+                       struct bt_ctf_clock_class *clock_class =
+                               bt_ctf_field_type_integer_get_mapped_clock_class(
+                                       field_type);
+
+                       bt_put(clock_class);
+                       if (!clock_class) {
+                               BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be mapped to a clock class: "
+                                       "timestamp-begin-ft-addr=%p", field_type);
+                               goto invalid;
+                       }
+               }
+
+               BT_PUT(field_type);
+       }
+
+       /*
+        * If there's a `timestamp_end` field, it must be an unsigned
+        * integer field type. Also, if the trace is not a CTF writer's
+        * trace, then we cannot automatically set the mapped clock
+        * class of this field, so it must have a mapped clock class.
+        */
+       field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_context_type, "timestamp_end");
+       if (field_type) {
+               if (!bt_ctf_field_type_is_integer(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `timestamp_end` field must be an integer field type: "
+                               "timestamp-end-ft-addr=%p, timestamp-end-ft-id=%s",
+                               field_type,
+                               bt_ctf_field_type_id_string(field_type->id));
+                       goto invalid;
+               }
+
+               if (bt_ctf_field_type_integer_is_signed(field_type)) {
+                       BT_LOGW("Invalid packet context field type: `timestamp_end` field must be an unsigned integer field type: "
+                               "timestamp-end-ft-addr=%p", field_type);
+                       goto invalid;
+               }
+
+               if (!trace->is_created_by_writer) {
+                       struct bt_ctf_clock_class *clock_class =
+                               bt_ctf_field_type_integer_get_mapped_clock_class(
+                                       field_type);
+
+                       bt_put(clock_class);
+                       if (!clock_class) {
+                               BT_LOGW("Invalid packet context field type: `timestamp_end` field must be mapped to a clock class: "
+                                       "timestamp-end-ft-addr=%p", field_type);
+                               goto invalid;
+                       }
+               }
+
+               BT_PUT(field_type);
+       }
+
+       goto end;
+
+invalid:
+       is_valid = false;
+
+end:
+       bt_put(field_type);
+       return is_valid;
+}
+
+static
+bool event_header_field_type_is_valid(struct bt_ctf_trace *trace,
+               struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_field_type *event_header_type)
+{
+       bool is_valid = true;
+       struct bt_ctf_field_type *field_type = NULL;
+
+       /*
+        * We do not validate that the `timestamp` field exists here
+        * because CTF does not require this exact name to be mapped to
+        * a clock class.
+        */
+
+       if (!event_header_type) {
+               /*
+                * No event header field type: stream class must have
+                * only one event class.
+                */
+               if (bt_ctf_stream_class_get_event_class_count(stream_class) > 1) {
+                       BT_LOGW_STR("Invalid event header field type: "
+                               "event header field type does not exist but there's more than one event class in the stream class.");
+                       goto invalid;
+               }
+
+               /* No event header field type: valid at this point */
+               goto end;
+       }
+
+       /* Event header field type, if it exists, must be a structure */
+       if (!bt_ctf_field_type_is_structure(event_header_type)) {
+               BT_LOGW("Invalid event header field type: must be a structure field type if it exists: "
+                       "ft-addr=%p, ft-id=%s",
+                       event_header_type,
+                       bt_ctf_field_type_id_string(event_header_type->id));
+               goto invalid;
+       }
+
+       /*
+        * If there's an `id` field, it must be an unsigned integer
+        * field type or an enumeration field type with an unsigned
+        * integer container field type.
+        */
+       field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               event_header_type, "id");
+       if (field_type) {
+               struct bt_ctf_field_type *int_ft;
+
+               if (bt_ctf_field_type_is_integer(field_type)) {
+                       int_ft = bt_get(field_type);
+               } else if (bt_ctf_field_type_is_enumeration(field_type)) {
+                       int_ft = bt_ctf_field_type_enumeration_get_container_type(
+                               field_type);
+               } else {
+                       BT_LOGW("Invalid event header field type: `id` field must be an integer or enumeration field type: "
+                               "id-ft-addr=%p, id-ft-id=%s",
+                               field_type,
+                               bt_ctf_field_type_id_string(field_type->id));
+                       goto invalid;
+               }
+
+               assert(int_ft);
+               if (bt_ctf_field_type_integer_is_signed(int_ft)) {
+                       BT_LOGW("Invalid event header field type: `id` field must be an unsigned integer or enumeration field type: "
+                               "id-ft-addr=%p", int_ft);
+                       goto invalid;
+               }
+
+               bt_put(int_ft);
+               BT_PUT(field_type);
+       }
+
+       goto end;
+
+invalid:
+       is_valid = false;
+
+end:
+       bt_put(field_type);
+       return is_valid;
+}
+
 int bt_ctf_trace_add_stream_class(struct bt_ctf_trace *trace,
                struct bt_ctf_stream_class *stream_class)
 {
@@ -678,6 +1127,49 @@ int bt_ctf_trace_add_stream_class(struct bt_ctf_trace *trace,
                }
        }
 
+       /*
+        * At this point all the field types in the validation output
+        * are valid. Validate the semantics of some scopes according to
+        * the CTF specification.
+        */
+       if (!packet_header_field_type_is_valid(trace,
+                       trace_sc_validation_output.packet_header_type)) {
+               ret = -1;
+               goto end;
+       }
+
+       if (!packet_context_field_type_is_valid(trace,
+                       stream_class,
+                       trace_sc_validation_output.packet_context_type)) {
+               ret = -1;
+               goto end;
+       }
+
+       if (!event_header_field_type_is_valid(trace,
+                       stream_class,
+                       trace_sc_validation_output.event_header_type)) {
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * Now is the time to automatically map specific field types of
+        * the stream class's packet context and event header field
+        * types to the stream class's clock's class if they are not
+        * mapped to a clock class yet. We do it here because we know
+        * that after this point, everything is frozen so it won't be
+        * possible for the user to modify the stream class's clock, or
+        * to map those field types to other clock classes.
+        */
+       if (trace->is_created_by_writer) {
+               if (bt_ctf_stream_class_map_clock_class(stream_class,
+                               trace_sc_validation_output.packet_context_type,
+                               trace_sc_validation_output.event_header_type)) {
+                       ret = -1;
+                       goto end;
+               }
+       }
+
        bt_object_set_parent(stream_class, trace);
        g_ptr_array_add(trace->stream_classes, stream_class);
 
@@ -907,7 +1399,7 @@ int append_trace_metadata(struct bt_ctf_trace *trace,
                struct metadata_context *context)
 {
        unsigned char *uuid = trace->uuid;
-       int ret;
+       int ret = 0;
 
        if (trace->native_byte_order == BT_CTF_BYTE_ORDER_NATIVE) {
                ret = -1;
@@ -933,15 +1425,17 @@ int append_trace_metadata(struct bt_ctf_trace *trace,
        g_string_append_printf(context->string, "\tbyte_order = %s;\n",
                get_byte_order_string(trace->native_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->packet_header_type,
-               context);
-       if (ret) {
-               goto end;
+       if (trace->packet_header_type) {
+               g_string_append(context->string, "\tpacket.header := ");
+               context->current_indentation_level++;
+               g_string_assign(context->field_name, "");
+               ret = bt_ctf_field_type_serialize(trace->packet_header_type,
+                       context);
+               if (ret) {
+                       goto end;
+               }
+               context->current_indentation_level--;
        }
-       context->current_indentation_level--;
 
        g_string_append(context->string, ";\n};\n\n");
 end:
index 324158d41a0e264de94407a15abd3483d8b2bc73..5dd3145b00d63b6dcf7a24e7b7cd1676290803a1 100644 (file)
@@ -2165,6 +2165,11 @@ void test_empty_stream(struct bt_ctf_writer *writer)
                goto end;
        }
 
+       ret = bt_ctf_stream_class_set_packet_context_type(stream_class, NULL);
+       assert(ret == 0);
+       ret = bt_ctf_stream_class_set_event_header_type(stream_class, NULL);
+       assert(ret == 0);
+
        ok(bt_ctf_stream_class_get_trace(NULL) == NULL,
                "bt_ctf_stream_class_get_trace handles NULL correctly");
        ok(bt_ctf_stream_class_get_trace(stream_class) == NULL,
@@ -2555,6 +2560,10 @@ void test_create_writer_vs_non_writer_mode(void)
        /* Create writer, writer stream class, stream, and clock */
        writer = bt_ctf_writer_create(trace_path);
        assert(writer);
+       writer_clock = bt_ctf_clock_create("writer_clock");
+       assert(writer_clock);
+       ret = bt_ctf_writer_add_clock(writer, writer_clock);
+       assert(!ret);
        ret = bt_ctf_writer_set_byte_order(writer, BT_CTF_BYTE_ORDER_LITTLE_ENDIAN);
        assert(!ret);
        writer_trace = bt_ctf_writer_get_trace(writer);
@@ -2564,16 +2573,14 @@ void test_create_writer_vs_non_writer_mode(void)
        ret = bt_ctf_stream_class_set_event_header_type(writer_sc,
                empty_struct_ft);
        assert(!ret);
+       ret = bt_ctf_stream_class_set_clock(writer_sc, writer_clock);
+       assert(!ret);
        ret = bt_ctf_trace_add_stream_class(writer_trace, writer_sc);
        assert(!ret);
        writer_stream = bt_ctf_stream_create(writer_sc, writer_stream_name);
        assert(writer_stream);
        ok(!strcmp(bt_ctf_stream_get_name(writer_stream), writer_stream_name),
                "bt_ctf_stream_get_name() returns the stream's name");
-       writer_clock = bt_ctf_clock_create("writer_clock");
-       assert(writer_clock);
-       ret = bt_ctf_writer_add_clock(writer, writer_clock);
-       assert(!ret);
 
        /* Create non-writer trace, stream class, stream, and clock */
        non_writer_trace = bt_ctf_trace_create();
@@ -2586,6 +2593,8 @@ void test_create_writer_vs_non_writer_mode(void)
        ret = bt_ctf_stream_class_set_event_header_type(non_writer_sc,
                empty_struct_ft);
        assert(!ret);
+       ret = bt_ctf_stream_class_set_packet_context_type(non_writer_sc, NULL);
+       assert(!ret);
        ret = bt_ctf_trace_add_stream_class(non_writer_trace, non_writer_sc);
        assert(!ret);
        non_writer_stream = bt_ctf_stream_create(non_writer_sc, NULL);
@@ -2778,6 +2787,8 @@ void test_static_trace(void)
        assert(ret == 0);
        stream_class = bt_ctf_stream_class_create(NULL);
        assert(stream_class);
+       ret = bt_ctf_stream_class_set_packet_context_type(stream_class, NULL);
+       assert(ret == 0);
        ret = bt_ctf_trace_add_stream_class(trace, stream_class);
        assert(ret == 0);
        stream = bt_ctf_stream_create(stream_class, "hello");
This page took 0.04043 seconds and 4 git commands to generate.