lib: decouple variant FC option names from selector FC mapping names
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 12 Jul 2019 19:03:40 +0000 (15:03 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Thu, 18 Jul 2019 13:42:13 +0000 (09:42 -0400)
commit45c51519900e100d9acda4acb9516ef69bc2d045
tree642159660a8bbcde16e2229442d7e216acf09717
parent19bb8b5a08e1ee2aadd42bf11f7ed8a62f36bd5b
lib: decouple variant FC option names from selector FC mapping names

This patch makes the options of a variant field class (FC) which has a
selector have their own range set, decoupled from any mapping of the
selector FC.

Motivation and solution
=======================
In CTF 1.8, structure FC member and variant FC option names which start
with `_` in the metadata stream must be "unescaped" by removing the `_`
prefix. For example:

    struct {
        string _salut;
        string meow;
    };

`_salut` becomes `salut` in trace IR, while `meow` stays `meow`.

CTF 1.8.2 specifies:

> Fields starting with an underscore should have their leading
> underscore removed by the CTF trace readers.

Here, we interpret "should" as "must" because Babeltrace 1, Trace
Compass, and other CTF consumers honor this recommandation.

It is not specified, however, that this strategy applies to enumeration
FC mapping labels. For example:

    enum {
        _SALUT,
        MEOW,
    };

In Babeltrace 1 (`text` output format and API), `_SALUT` and `MEOW`
remain as is, so Babeltrace 2 should do the same.

There's an issue however when an enumeration FC is used as a tag, or
selector, for a variant FC:

    enum {
        _salut,
        _meow,
    } tag;

    variant <tag> {
        string _salut;
        int _meow;
    };

This is valid TSDL 1.8, but once in trace IR, the enumeration FC mapping
labels need to stay as is, while the variant FC option names need to be
unescaped. Once in trace IR, the equivalent would be:

    enum {
        _salut,
        _meow,
    } tag;

    variant <tag> {
        string salut;
        int meow;
    };

Before this patch, this is not valid because, when a variant FC has a
selector FC, the option and mapping names must match exactly: we don't
want to bring this CTF-specific escaping logic into the CTF-agnostic
API.

The current hack to avoid this, performed by `src.ctf.fs`, is to also
unescape the mapping names, so as to get:

    enum {
        salut,
        meow,
    } tag;

    variant <tag> {
        string salut;
        int meow;
    };

However, this makes Babeltrace 2 not behave like Babeltrace 1 because
the enumeration FC mapping names are different. For example, the
`sink.text.pretty` outputs differ for the same CTF trace.

The solution brought by this patch to fix this issue is to make the
options of a variant FC have their own range set. Using the example
above, the layout in trace IR becomes:

    enum {
        _salut,
        _meow,
    } tag;

    variant <tag> {
        string salut {0};
        int meow {1};
    };

where `{0}` and `{1}` are the range sets associated to the option.

Here's another example:

    enum {
        _salut,
        _meow = 12,
        coucou = 17 ... 45,
        _meow = 1 ... 5,
    } tag;

    variant <tag> {
        string salut {0};
        int meow {1 ... 5, 12};
        int coucou[5] {17 ... 45};
    };

This change allows `src.ctf.fs` to keep the enumeration FC mapping names
as is while properly escaping the variant FC option names.

Library changes
===============
The simple variant field class type is replaced with three new variant
field class types:

Variant without a selector:
    You can create a variant FC without a selector FC, just like before,
    with bt_field_class_variant_create(), passing `NULL` as the
    `selector_field_class` parameter.

    The field class's type is
    `BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR`.

    You can use bt_field_class_variant_without_selector_append_option()
    to append an option (name and FC) to a variant FC without a
    selector.

    You can use bt_field_class_variant_borrow_option_by_index_const()
    and bt_field_class_variant_borrow_option_by_name_const() to borrow
    a variant FC (base) option from a variant FC without a selector.

Variant with a selector:
    You can create a variant FC with a specific selector FC with
    bt_field_class_variant_create().

    The selector FC must be an _integer_ FC. This is less strict than
    before: because each option has its own range set, the selector FC
    does not need to be an enumeration FC. It can be an enumeration FC,
    but there's no association between its mapping names and the variant
    FC option names.

    The field class types are
    `BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR` and
    `BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_SELECTOR`.

    There's an unsigned and a signed type to make the API typing more
    strict: a variant FC with an unsigned selector has specific option
    objects from which you can borrow unsigned integer range sets.

    You can use
    bt_field_class_variant_with_unsigned_selector_append_option() and
    bt_field_class_variant_with_signed_selector_append_option() to
    append an option (name, FC, and range set) to a variant FC with a
    selector.

    You can use
    bt_field_class_variant_with_unsigned_selector_borrow_option_by_index_const(),
    bt_field_class_variant_with_unsigned_selector_borrow_option_by_name_const(),
    bt_field_class_variant_with_signed_selector_borrow_option_by_index_const(),
    or
    bt_field_class_variant_with_signed_selector_borrow_option_by_name_const()
    to borrow a variant FC with a selector option from a variant FC
    with a selector.

    The option object's type is either
    `bt_field_class_variant_with_unsigned_selector_option` or
    `bt_field_class_variant_with_signed_selector_option`. You can
    convert it to a base option (to get its name and borrow its field
    class) with
    bt_field_class_variant_with_unsigned_selector_option_as_option_const()
    or
    bt_field_class_variant_with_signed_selector_option_as_option_const().

    You can borrow the ranges of a variant FC with a selector option
    with
    bt_field_class_variant_with_unsigned_selector_option_borrow_ranges_const()
    or
    bt_field_class_variant_with_signed_selector_option_borrow_ranges_const().

    You can use
    bt_field_class_variant_with_selector_borrow_selector_field_path_const()
    to borrow the selector field path from a variant FC with a selector.

I also added the following functions for convenience:

* bt_field_variant_borrow_selected_class_option_const()
* bt_field_variant_with_unsigned_selector_borrow_selected_class_option_const()
* bt_field_variant_with_signed_selector_borrow_selected_class_option_const()

For consistency, bt_field_variant_select_option_field() is renamed to
bt_field_variant_select_option_field_by_index().

This patch also makes an enumeration FC mapping contain an integer range
set object. This was planned anyway, and not doing it here would have
meant to duplicate and adapt code for the variant FC option ranges, for
example in `sink.text.details` to sort the ranges of a range set.

This means that bt_field_class_unsigned_enumeration_map_range() and
bt_field_class_signed_enumeration_map_range() are replaced with
bt_field_class_unsigned_enumeration_add_mapping() and
bt_field_class_signed_enumeration_add_mapping() which accept resp.
unsigned and signed integer range sets.

This also means that
bt_field_class_enumeration_mapping_get_range_count(),
bt_field_class_unsigned_enumeration_mapping_get_range_by_index(), and
bt_field_class_signed_enumeration_mapping_get_range_by_index() are
replaced with
bt_field_class_unsigned_enumeration_mapping_borrow_ranges_const() and
bt_field_class_signed_enumeration_mapping_borrow_ranges_const().

Because I needed it when adapting the project's plugins, I also added
the following functions for convenience:

* bt_field_class_unsigned_enumeration_borrow_mapping_by_label_const()
* bt_field_class_signed_enumeration_borrow_mapping_by_label_const()

Noteworthy plugin changes
=========================
`src.ctf.fs`:
    * Enumeration FC mapping names are not unescaped anymore: they are
      kept as is.

    * A CTF IR named FC contains the original name and the escaped name.

      The original name is used to find the ranges of a variant FC
      option in the selector FC as the mapping names are not escaped
      anymore, so they potentially do not match the variant FC option
      names.

    * When translating a CTF IR variant FC to a trace IR variant FC, the
      trace IR selector (enumeration) FC's integer range set references
      are reused directly to append the corresponding variant FC
      options.

`sink.ctf.fs`:
    * Enumeration FC mapping names are not escaped anymore: they are
      kept as is.

    * If a variant FC has a selector, then for each option, the
      component finds the corresponding mapping in the selector FC _by
      range set_ to know whether or not to escape the option name.

      This is because this must work (from `src.ctf.fs`):

          enum {
              salut,
              _meow,
          } tag;

          variant <tag> {
              string salut;
              int _meow;
          };

      Once in trace IR, the `_meow` option becomes `meow`, but the
      `_meow` mapping keeps its original name. However, we know that,
      for `src.ctf.fs`, the range sets match exactly. For the `meow`
      option, the corresponding mapping is `_meow` because they both
      have the range set with the single range [1, 1]. In that case,
      when going back to TSDL, `sink.ctf.fs` writes `_meow` for the
      option name, while `salut` remains `salut`.

      I added new `sink.ctf.fs` tests, with new succeeding CTF traces,
      to verify that the component works as expected with those specific
      cases.

      If there's any issue when doing this, `sink.ctf.fs` falls back to
      creating a dedicated selector FC for the variant FC. For example,
      in trace IR, this is totally valid:

          enum {
              a = 2,
              b = 5,
              d = 8,
          } tag;

          variant <tag> {
              string a {2};
              int b {11};
              int c[22] {15 ... 19};
          };

      because there's no association between mapping names and option
      names. This too:

          int tag;

          variant <tag> {
              string a {2};
              int b {11};
              int c[22] {15 ... 19};
          };

      Those specimens cannot be translated to TSDL 1.8 however.

    * Because of changes in the way TSDL identifers are protected and
      validated, clock class names are not systematically escaped if
      it's not needed. Therefore the clock class name `default` remains
      `default`; it does not become `_default` like before.

`sink.text.details`:
    * Variant FC option ranges are written next to the option's name:

          var: Variant (unsigned selector) (3 options, Selector field path [Event payload: 0]):
            COSSETTE: [0]: String
            _PELCHAT: [1]: String
            VOISINE: [2] [5, 19]: String

    * Enumeration FC mapping ranges are written next to the option's
      name instead of having one per line. I find this is more compact
      and easier to read as mappings typically do not contain a lot of
      ranges:

          tag: Unsigned enumeration (8-bit, Base 10, 3 mappings):
            COSSETTE: [0]
            VOISINE: [2] [5, 19]
            __PELCHAT: [1]

Python bindings changes
=======================
Essentially, the `bt2` Python package is updated to match the library's
API changes:

* `_EnumerationFieldClassMapping` is not a set anymore: it has
  a `ranges` property (which is a set).

* _EnumerationFieldClass.map_range() is replaced with
  _EnumerationFieldClass.add_mapping() to which you pass an integer
  range set.

* _EnumerationFieldClass.labels_for_value() is replaced with
  _EnumerationFieldClass.mappings_for_value() to get mappings instead of
  simple labels.

* _EnumerationFieldClass.__getitem__() now uses
  bt_field_class_unsigned_enumeration_borrow_mapping_by_label_const()
  or
  bt_field_class_signed_enumeration_borrow_mapping_by_label_const().

* The new `_VariantFieldClassWithSelectorOption` object inherits
  `_VariantFieldClassOption`, adding a `ranges` property.

* With a `_VariantFieldClass`, you can borrow base options
  (`_VariantFieldClassOption`) to get their names and field classes.

* The new types `_VariantFieldClassWithoutSelector`,
  `_VariantFieldClassWithUnsignedSelector`, and
  `_VariantFieldClassWithSignedSelector` are analogous to the new
  types in the library's API.

  You can create them with
  _TraceClass.create_variant_field_class_without_selector() and
  _TraceClass.create_variant_field_class_with_selector(). The selector
  FC object must be an instance of `_IntegerFieldClass`.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: I084b03ea816ff8bee03ef5315c24fa24cfe74d80
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1717
Tested-by: jenkins <jenkins@lttng.org>
54 files changed:
include/babeltrace2/trace-ir/field-class-const.h
include/babeltrace2/trace-ir/field-class.h
include/babeltrace2/trace-ir/field-const.h
include/babeltrace2/trace-ir/field.h
include/babeltrace2/types.h
src/bindings/python/bt2/bt2/field.py
src/bindings/python/bt2/bt2/field_class.py
src/bindings/python/bt2/bt2/native_bt_field_class.i
src/bindings/python/bt2/bt2/trace_class.py
src/common/common.h
src/lib/lib-logging.c
src/lib/trace-ir/field-class.c
src/lib/trace-ir/field-class.h
src/lib/trace-ir/field.c
src/lib/trace-ir/field.h
src/lib/trace-ir/resolve-field-path.c
src/plugins/ctf/common/metadata/ctf-meta-resolve.c
src/plugins/ctf/common/metadata/ctf-meta-translate.c
src/plugins/ctf/common/metadata/ctf-meta.h
src/plugins/ctf/common/metadata/visitor-generate-ir.c
src/plugins/ctf/common/msg-iter/msg-iter.c
src/plugins/ctf/fs-sink/fs-sink-ctf-meta.h
src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c
src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c
src/plugins/lttng-utils/debug-info/trace-ir-data-copy.c
src/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.c
src/plugins/text/details/write.c
src/plugins/text/pretty/print.c
tests/Makefile.am
tests/bindings/python/bt2/test_event.py
tests/bindings/python/bt2/test_field.py
tests/bindings/python/bt2/test_field_class.py
tests/bindings/python/bt2/test_graph.py
tests/bindings/python/bt2/test_message.py
tests/bindings/python/bt2/test_message_iterator.py
tests/bindings/python/bt2/test_packet.py
tests/data/ctf-traces/succeed/meta-variant-no-underscore/metadata [new file with mode: 0644]
tests/data/ctf-traces/succeed/meta-variant-no-underscore/stream [new file with mode: 0644]
tests/data/ctf-traces/succeed/meta-variant-one-underscore/metadata [new file with mode: 0644]
tests/data/ctf-traces/succeed/meta-variant-one-underscore/stream [new file with mode: 0644]
tests/data/ctf-traces/succeed/meta-variant-reserved-keywords/metadata [new file with mode: 0644]
tests/data/ctf-traces/succeed/meta-variant-reserved-keywords/stream [new file with mode: 0644]
tests/data/ctf-traces/succeed/meta-variant-same-with-underscore/metadata [new file with mode: 0644]
tests/data/ctf-traces/succeed/meta-variant-same-with-underscore/stream [new file with mode: 0644]
tests/data/ctf-traces/succeed/meta-variant-two-underscores/metadata [new file with mode: 0644]
tests/data/ctf-traces/succeed/meta-variant-two-underscores/stream [new file with mode: 0644]
tests/data/plugins/sink.ctf.fs/succeed/trace-double.expect
tests/data/plugins/sink.ctf.fs/succeed/trace-float.expect
tests/data/plugins/sink.ctf.fs/succeed/trace-meta-variant-no-underscore.expect [new file with mode: 0644]
tests/data/plugins/sink.ctf.fs/succeed/trace-meta-variant-one-underscore.expect [new file with mode: 0644]
tests/data/plugins/sink.ctf.fs/succeed/trace-meta-variant-reserved-keywords.expect [new file with mode: 0644]
tests/data/plugins/sink.ctf.fs/succeed/trace-meta-variant-same-with-underscore.expect [new file with mode: 0644]
tests/data/plugins/sink.ctf.fs/succeed/trace-meta-variant-two-underscores.expect [new file with mode: 0644]
tests/plugins/sink.ctf.fs/succeed/test_succeed
This page took 0.031852 seconds and 4 git commands to generate.