lib: do not allow any mapped clock class in trace's packet header FT
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Wed, 20 Dec 2017 22:33:15 +0000 (17:33 -0500)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Thu, 2 May 2019 03:32:02 +0000 (23:32 -0400)
Do not allow any integer field type to be mapped to a clock class within
the packet header field type of a trace.

This is one of the steps to ensure that a given stream class has only
one (if any) mapped clock class, recursively. More than one clock class
for a given stream class was never well supported anyway in CTF 1.8
because you cannot have multiple `timestamp_begin` and `timestamp_end`
fields.

It's also weird to have a clock value update in the packet header
because we don't have the value of `timestamp_begin` yet. Where would
this value be "located" on the timeline if `timestamp_begin` is the
absolute beginning timestamp of the packet containing said header?
Unless it is always the exact same value as `timestamp_begin` (which is
useless), it does not fit with the clock value update mechanism where,
as fields are decoded (or encoded), a given clock's value must be
updated monotonically: if the clock value found in the packet header is
less than `timestamp_begin`, then its lower bound is not
`timestamp_begin`.

The check is performed whenever the trace is about to be frozen (this
freezes the packet header field type) on success, that is:

* bt_trace_add_stream_class()
* bt_trace_set_is_static()

The check uses the new bt_validate_single_clock_class() internal
function. This one makes sure that a given field type recursively
contains only one clock class. You pass the expected clock class to the
function, or NULL so that the function sets it for you. In the trace
packet header field type case, even if this function returns 0
(success), that is, the field contains _at most_ one clock class, we
check that the returned expected clock class is still NULL to make sure
the field type in fact contains none.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
include/Makefile.am
include/babeltrace/ctf-ir/utils-internal.h [new file with mode: 0644]
lib/ctf-ir/trace.c
lib/ctf-ir/utils.c

index 669e003bd620a80db0ba295cd109aa55237a9b40..80cd4ddaa3e6e28aba8b9e5262bef66eb9713ebb 100644 (file)
@@ -188,6 +188,7 @@ noinst_HEADERS = \
        babeltrace/ctf-ir/trace-internal.h \
        babeltrace/ctf-ir/validation-internal.h \
        babeltrace/ctf-ir/visitor-internal.h \
+       babeltrace/ctf-ir/utils-internal.h \
        babeltrace/ctf-writer/clock-internal.h \
        babeltrace/ctf-writer/functor-internal.h \
        babeltrace/ctf-writer/serialize-internal.h \
diff --git a/include/babeltrace/ctf-ir/utils-internal.h b/include/babeltrace/ctf-ir/utils-internal.h
new file mode 100644 (file)
index 0000000..ac5612f
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef BABELTRACE_CTF_IR_UTILS_INTERNAL_H
+#define BABELTRACE_CTF_IR_UTILS_INTERNAL_H
+
+/*
+ * Babeltrace - Internal CTF IR utilities
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/ctf-ir/field-types.h>
+#include <stdint.h>
+
+BT_HIDDEN
+int bt_validate_single_clock_class(struct bt_field_type *field_type,
+               struct bt_clock_class **expected_clock_class);
+
+#endif /* BABELTRACE_CTF_IR_UTILS_INTERNAL_H */
index e21bf0eb4ef03485fbcda336dbd725b60729e534..7a583862e7a76b8aeab3ca2484bb78bcafdcc589 100644 (file)
@@ -43,6 +43,7 @@
 #include <babeltrace/ctf-ir/validation-internal.h>
 #include <babeltrace/ctf-ir/visitor-internal.h>
 #include <babeltrace/ctf-ir/utils.h>
+#include <babeltrace/ctf-ir/utils-internal.h>
 #include <babeltrace/compiler-internal.h>
 #include <babeltrace/values.h>
 #include <babeltrace/values-internal.h>
@@ -1108,6 +1109,34 @@ end:
        return is_valid;
 }
 
+static
+int check_packet_header_type_has_no_clock_class(struct bt_trace *trace)
+{
+       int ret = 0;
+
+       if (trace->packet_header_type) {
+               struct bt_clock_class *clock_class = NULL;
+
+               ret = bt_validate_single_clock_class(trace->packet_header_type,
+                       &clock_class);
+               bt_put(clock_class);
+               if (ret || clock_class) {
+                       BT_LOGW("Trace's packet header field type cannot "
+                               "contain a field type which is mapped to "
+                               "a clock class: "
+                               "trace-addr=%p, trace-name=\"%s\", "
+                               "clock-class-name=\"%s\"",
+                               trace, bt_trace_get_name(trace),
+                               clock_class ?
+                                       bt_clock_class_get_name(clock_class) :
+                                       NULL);
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
 int bt_trace_add_stream_class(struct bt_trace *trace,
                struct bt_stream_class *stream_class)
 {
@@ -1216,6 +1245,12 @@ int bt_trace_add_stream_class(struct bt_trace *trace,
                }
        }
 
+       ret = check_packet_header_type_has_no_clock_class(trace);
+       if (ret) {
+               /* check_packet_header_type_has_no_clock_class() logs errors */
+               goto end;
+       }
+
        /*
         * We're about to freeze both the trace and the stream class.
         * Also, each event class contained in this stream class are
@@ -2164,6 +2199,12 @@ int bt_trace_set_is_static(struct bt_trace *trace)
                goto end;
        }
 
+       ret = check_packet_header_type_has_no_clock_class(trace);
+       if (ret) {
+               /* check_packet_header_type_has_no_clock_class() logs errors */
+               goto end;
+       }
+
        trace->is_static = BT_TRUE;
        bt_trace_freeze(trace);
        BT_LOGV("Set trace static: addr=%p, name=\"%s\"",
index 9eff0508b31c01ba3e8b228ce69a86f844126a47..fba66fd5af7141c399e4097e310cf937f671aa7d 100644 (file)
@@ -33,6 +33,8 @@
 #include <stdlib.h>
 #include <glib.h>
 #include <babeltrace/ctf-ir/utils.h>
+#include <babeltrace/ctf-ir/field-types.h>
+#include <babeltrace/ref.h>
 
 static
 const char * const reserved_keywords_str[] = {"align", "callsite",
@@ -122,3 +124,140 @@ bt_bool bt_identifier_is_valid(const char *identifier)
 {
        return bt_validate_identifier(identifier) == 0;
 }
+
+BT_HIDDEN
+int bt_validate_single_clock_class(struct bt_field_type *field_type,
+               struct bt_clock_class **expected_clock_class)
+{
+       int ret = 0;
+       assert(field_type);
+       assert(expected_clock_class);
+
+       switch (bt_field_type_get_type_id(field_type)) {
+       case BT_FIELD_TYPE_ID_INTEGER:
+       {
+               struct bt_clock_class *mapped_clock_class =
+                       bt_field_type_integer_get_mapped_clock_class(field_type);
+
+               if (!mapped_clock_class) {
+                       goto end;
+               }
+
+               if (!*expected_clock_class) {
+                       /* Move reference to output parameter */
+                       *expected_clock_class = mapped_clock_class;
+                       mapped_clock_class = NULL;
+                       BT_LOGV("Setting expected clock class: "
+                               "expected-clock-class-addr=%p",
+                               *expected_clock_class);
+               } else {
+                       if (mapped_clock_class != *expected_clock_class) {
+                               BT_LOGW("Integer field type is not mapped to "
+                                       "the expected clock class: "
+                                       "mapped-clock-class-addr=%p, "
+                                       "expected-clock-class-addr=%p",
+                                       mapped_clock_class,
+                                       *expected_clock_class);
+                               bt_put(mapped_clock_class);
+                               ret = -1;
+                               goto end;
+                       }
+               }
+
+               bt_put(mapped_clock_class);
+               break;
+       }
+       case BT_FIELD_TYPE_ID_ENUM:
+       case BT_FIELD_TYPE_ID_ARRAY:
+       case BT_FIELD_TYPE_ID_SEQUENCE:
+       {
+               struct bt_field_type *subtype = NULL;
+
+               switch (bt_field_type_get_type_id(field_type)) {
+               case BT_FIELD_TYPE_ID_ENUM:
+                       subtype = bt_field_type_enumeration_get_container_type(
+                               field_type);
+                       break;
+               case BT_FIELD_TYPE_ID_ARRAY:
+                       subtype = bt_field_type_array_get_element_type(
+                               field_type);
+                       break;
+               case BT_FIELD_TYPE_ID_SEQUENCE:
+                       subtype = bt_field_type_sequence_get_element_type(
+                               field_type);
+                       break;
+               default:
+                       BT_LOGF("Unexpected field type ID: id=%d",
+                               bt_field_type_get_type_id(field_type));
+                       abort();
+               }
+
+               assert(subtype);
+               ret = bt_validate_single_clock_class(subtype,
+                       expected_clock_class);
+               bt_put(subtype);
+               break;
+       }
+       case BT_FIELD_TYPE_ID_STRUCT:
+       {
+               uint64_t i;
+               int64_t count = bt_field_type_structure_get_field_count(
+                       field_type);
+
+               for (i = 0; i < count; i++) {
+                       const char *name;
+                       struct bt_field_type *member_type;
+
+                       ret = bt_field_type_structure_get_field_by_index(
+                               field_type, &name, &member_type, i);
+                       assert(ret == 0);
+                       ret = bt_validate_single_clock_class(member_type,
+                               expected_clock_class);
+                       bt_put(member_type);
+                       if (ret) {
+                               BT_LOGW("Structure field type's field's type "
+                                       "is not recursively mapped to the "
+                                       "expected clock class: "
+                                       "field-ft-addr=%p, field-name=\"%s\"",
+                                       member_type, name);
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case BT_FIELD_TYPE_ID_VARIANT:
+       {
+               uint64_t i;
+               int64_t count = bt_field_type_variant_get_field_count(
+                       field_type);
+
+               for (i = 0; i < count; i++) {
+                       const char *name;
+                       struct bt_field_type *member_type;
+
+                       ret = bt_field_type_variant_get_field_by_index(
+                               field_type, &name, &member_type, i);
+                       assert(ret == 0);
+                       ret = bt_validate_single_clock_class(member_type,
+                               expected_clock_class);
+                       bt_put(member_type);
+                       if (ret) {
+                               BT_LOGW("Variant field type's field's type "
+                                       "is not recursively mapped to the "
+                                       "expected clock class: "
+                                       "field-ft-addr=%p, field-name=\"%s\"",
+                                       member_type, name);
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret;
+}
This page took 0.030469 seconds and 4 git commands to generate.