deliverable/barectf.git
3 years agoAdd dynamic array tracing tests C-0001
Philippe Proulx [Thu, 10 Sep 2020 21:32:50 +0000 (17:32 -0400)] 
Add dynamic array tracing tests

The new tests are modified copies of static array tracing tests as a
dynamic array field can contain the same fields as a static array field.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoRemoved unused `tests/conftest.py`
Philippe Proulx [Thu, 10 Sep 2020 20:54:58 +0000 (16:54 -0400)] 
Removed unused `tests/conftest.py`

Used to contain the yaml_cfg_path() fixture; not needed anymore since
b1c5cbc ("tests/config/yaml: use pytest_collect_file() hook for YAML
files") and 816fefd ("tests/tracing: use pytest_collect_file() hook for
YAML files").

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotests/tracing/support/test-platform.c: clear buffer at every packet opening
Philippe Proulx [Thu, 10 Sep 2020 19:01:56 +0000 (15:01 -0400)] 
tests/tracing/support/test-platform.c: clear buffer at every packet opening

This ensures that there's no garbage data left in the buffer between
packets.

It's not triggering any failure currently because all the tests in
`tests/tracing` produce a single packet.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotracing/src/succeed/static-array: update copyright notice's year
Philippe Proulx [Thu, 10 Sep 2020 19:01:36 +0000 (15:01 -0400)] 
tracing/src/succeed/static-array: update copyright notice's year

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agosrc/succeed/static-array: add missing `const` qualifiers
Philippe Proulx [Thu, 10 Sep 2020 18:58:14 +0000 (14:58 -0400)] 
src/succeed/static-array: add missing `const` qualifiers

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotests/tracing: use pytest_collect_file() hook for YAML files
Philippe Proulx [Thu, 10 Sep 2020 18:41:12 +0000 (14:41 -0400)] 
tests/tracing: use pytest_collect_file() hook for YAML files

This patch applies the same strategy as b1c5cbc ("tests/config/yaml: use
pytest_collect_file() hook for YAML files") to `tests/tracing`.

This patch changes `tests/tracing/conftest.py` so that it implements a
pytest_collect_file() Pytest hook to create Pytest file and item objects
for each barectf YAML configuration file found in the `configs`
directory. Thanks to the base name of this YAML file,
pytest_collect_file() automatically finds the corresponding
test-specific C source and expectation files.

This makes is possible to remove `test_succeed_static_array.py` and
avoid duplication and mismatches between existing YAML files and
available test functions.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotests/config/yaml: use pytest_collect_file() hook for YAML files
Philippe Proulx [Thu, 10 Sep 2020 04:06:41 +0000 (00:06 -0400)] 
tests/config/yaml: use pytest_collect_file() hook for YAML files

This patch changes `tests/config/yaml/conftest.py` so that it implements
a pytest_collect_file() Pytest hook to create Pytest file and item
objects for each failing barectf YAML configuration file found.

This makes is possible to remove all `test_fail_*.py` files and avoid
duplication and mismatches between existing YAML files and available
test functions.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoconfig.py, tsdl182gen.py: fix Mypy/Pylint errors
Philippe Proulx [Thu, 10 Sep 2020 01:36:54 +0000 (21:36 -0400)] 
config.py, tsdl182gen.py: fix Mypy/Pylint errors

3 years agoAdd static array tracing tests
Philippe Proulx [Wed, 9 Sep 2020 23:35:11 +0000 (19:35 -0400)] 
Add static array tracing tests

This patch adds static array tracing tests, also adding the testing
infrastructure for other such tests.

In `tests/tracing`, the test functions of `test_succeed_static_array.py`
use the tracing_succeed_test() fixture as found in
`tests/tracing/conftest.py`.

tracing_succeed_test() does the following:

 1. Creates a temporary directory with the tmpdir() fixture.

 2. Automatically finds the paths, based on the test's module and
    function names, of:

    * A YAML configuration file.

    * A test-specific C source file.

    * Two expectation files (one for the metadata stream and one for the
      data stream).

 3. Creates a barectf configuration from the YAML file found in 2.

 4. Generates the C code files using the barectf configuration of 3.,
    writing the files to the temporary directory of 1.

 5. Generates the metadata stream using the barectf configuration of 3.,
    stripping some variable version and generation date lines.

    This step does not write any file to the file system.

 6. Copies the files in the `tests/tracing/support` directory to the
    temporary directory of 1.

    `test-platform.c` and `test-platform.h` form the barectf platform
    for all the tracing tests.

 7. Copies the test-specific C source file found in 2. as `test.c` to
    the temporary directory of 1.

 8. Executes `make` in the temporary directory of 1.

 9. Executes `./test` in the temporary directory of 1.

10. Reads the produced data stream file (`stream` in the temporary
    directory of 1.) and compares it with the data stream expectation
    file found in 2.

11. Compares the metadata stream contents of 5. with the metadata stream
    expectation file found in 2.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoSimplify current tests
Philippe Proulx [Wed, 9 Sep 2020 22:02:17 +0000 (18:02 -0400)] 
Simplify current tests

This patch:

* Adds the global yaml_cfg_path() fixture which returns the path to an
  hypothetical barectf YAML configuration file based on the test's
  module file and function names.

* Updates the config_fail_test() fixture to only use yaml_cfg_path()

* Updates all the test functions to only use config_fail_test() and call
  it without parameters.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoFix: include/3/stdreal.yaml: `align` -> `alignment`
Philippe Proulx [Thu, 10 Sep 2020 01:14:22 +0000 (21:14 -0400)] 
Fix: include/3/stdreal.yaml: `align` -> `alignment`

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoAdd user dynamic array field support
Philippe Proulx [Wed, 9 Sep 2020 20:50:51 +0000 (16:50 -0400)] 
Add user dynamic array field support

This patch adds support for user dynamic array fields.

The element field type of a user dynamic array field type can be any of
the following:

* Bit array field type.
* String field type.
* Static array field type.

Note that it _cannot_ be another dynamic array field type.

The new `barectf.DynamicArrayFieldType` represents a dynamic array field
type. Its constructor accepts a length field type. As of this version,
this length field type must be within the same immediate structure field
type and before it.

The YAML ways to specify a dynamic array field type are:

barectf 2 configuration:
    class: array
    length: dynamic
    element-type:
      ...

barectf 3 configuration:
    class: dynamic-array
    element-field-type:
      ...

Note that in YAML, you don't specify the length field type: the parser
automatically creates a 32-bit, byte-aligned unsigned integer field type
before which, for a dynamic array field type named `a`, has the name
`__a_len`. This also becomes part of the corresponding tracing function
parameter's name.

In the future, I can add a `length-field-type` property to a barectf 3
YAML dynamic array field type to point to an anterior unsigned integer
field type using some kind of reference, for example:

    class: structure
    members:
      - my_length: uint16
      - my_array:
          field-type:
            class: dynamic-array
            length-field-type-name: my_length
            element-field-type:
              ...

This would make it possible for more than one dynamic array fields
to use the same length field, for example:

    class: structure
    members:
      - my_length: uint16
      - my_uint_array:
          field-type:
            class: dynamic-array
            length-field-type-name: my_length
            element-field-type: uint8
      - my_string_array:
          field-type:
            class: dynamic-array
            length-field-type-name: my_length
            element-field-type: string

The constructor of `barectf.StructureFieldType` calls
_set_dyn_array_ft_length_ft_member_names() which, for each member having
a dynamic array field type:

* Sets its `_length_ft_member_name` attribute to the name of the
  structure field type member having its length field type.

* Sets its length field type's `_is_len` attribute to `True`.

I consider those as hacks, but considering the current constraints, it
makes parts of the (barectf) code easier to implement and maintain.

The C code generation approach is similar to the static array field
case. The `*-write-static-array-statements.j2` templates are renamed to
`*-write-array-statements.j2` and use the `length_src` variable as the
loop's length's value. `*-write-static-array-statements.j2` and the new
`*-write-dynamic-array-statements.j2` set `length_src` before including
`*-write-array-statements.j2`.

To make things easier, barectf now systematically generates alignment
statements if the alignment is greater than one. This could be optimized
again in the future, considering arrays this time. The
`_WriteOp.offset_in_byte` optimization still exists, although as soon as
it's not statically known, it's now `None` and
`serialize-write-bit-array-statements.j2` uses the safe, dynamic
`ctx->at % 8` expression. try_create_align_op() does this, more or less:

    If `self._offset_in_byte` is not currently known and the requested
    alignment is 8:
      Set `self._offset_in_byte` to 0.
    Else:
      If we're currently within an array operation:
        Reset `self._offset_in_byte`.
      Else:
        If `self._offset_in_byte` is currently known:
          Align `self._offset_in_byte` with the requested alignment.

This ensures that each array field's element is aligned before being
written.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoconfig.py: UnsignedIntegerFieldType.__init__(): forward `**kwargs` too
Philippe Proulx [Wed, 9 Sep 2020 20:43:41 +0000 (16:43 -0400)] 
config.py: UnsignedIntegerFieldType.__init__(): forward `**kwargs` too

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoAdd user static array field support (with recursion)
Philippe Proulx [Wed, 9 Sep 2020 15:00:12 +0000 (11:00 -0400)] 
Add user static array field support (with recursion)

This patch adds support for user static array fields.

The element field type of a user static array field type can be any of
the following:

* Bit array field type.
* String field type.
* Static array field type.

The generated serializing C code does not assume anything about the
memory layout of a user array: it iterates each item to serialize them
individually.

For example, consider the following TSDL field type:

    class: static-array
    length: 3
    element-field-type:
      class: unsigned-integer
      size: 16

The tracing function's parameter's C type for the corresponding field is
`const uint16_t *`. Assuming the parameter's name is `a`, the
serialization function accesses `a[0]`, `a[1]`, and `a[2]`; it doesn't
copy large blocks of memory as is. This is because we don't know the
target architecture's alignment constraints and general memory layout.

For:

    class: static-array
    length: 3
    element-field-type:
      class: string

the parameter's C type is `const char * const *`.

Note that a pointed C type is always `const`, so the `barectf.h` user
might need to cast accordingly if her own C types miss such `const`
qualifiers. Here's another example:

    class: static-array
    length: 2
    element-field-type:
      class: static-array
      length: 3
      element-field-type:
        class: unsigned-integer
        size: 8

This becomes `const uint8_t * const *`.

As of this patch, a tracing function uses `uint32_t` as the loop index
type. I believe this will be enough for barectf's use cases. It could
(should) also be based on the static array field type's length.

Each loop is isolated within its own C scope. The loop index variable
names are `i`, then `j`, then `k`, then `k1`, then `k2`, and so on.

Notable changes
===============
`cgen.py`:
    * Add a level property to operation objects.

      This is the static array nesting level.

    * _OpBuilder._build_for_ft(): handle user static array field types.

      A user static array field type leads to an "align" operation
      (possibly) and its element field type's operation, all within a
      compound operation.

      The name of the element field type's operation is the C array
      subscript with the right index variable name, for example `[i]`.

      The packet header's UUID field type still has its special case.

    * Add _loop_var_name() function which returns the name of a loop
      index variable name based on a level (0 is `i`, 1 is `j`, and so
      on).

    * Add general template filter `loop_var_name` which is
      _loop_var_name().

    * Add general template filter `op_src_var_name` which returns an
      operation's source variable name.

      This used to be the op_src() macro in `c/common.j2`, but with
      array subscript names, it's too ugly for the Jinja 2 language.

    * _CodeGen._ft_c_type(): handle static array field types (return a
      `_PointerCType` object).

`config_parse_v3.py`:
    Accept user static array field types.

Existing templates:
    Remove op_src(); use the `op_src_var_name` filter instead.

`serialize-write-static-array-statements.j2`,
`size-write-static-array-statements.j2`:
    New templates to serialize and compute the size of static array
    fields.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoFix: config.py: implement _ArrayFieldType.size_is_dynamic()
Philippe Proulx [Wed, 9 Sep 2020 14:15:25 +0000 (10:15 -0400)] 
Fix: config.py: implement _ArrayFieldType.size_is_dynamic()

An array field type's size is dynamic if its element field type is.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoserialize-write-bit-array-statements.j2: increment position within scope
Philippe Proulx [Wed, 9 Sep 2020 01:52:42 +0000 (21:52 -0400)] 
serialize-write-bit-array-statements.j2: increment position within scope

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocgen.py: use C type classes for _CodeGen._ft_c_type()
Philippe Proulx [Wed, 9 Sep 2020 01:21:33 +0000 (21:21 -0400)] 
cgen.py: use C type classes for _CodeGen._ft_c_type()

This patch introduces new classes to represent C types in `cgen.py`.

The C type class hierarchy is:

    _CType
      _ArithCType
      _PointerCType

A `_PointerCType` object contains another `_CType` object.

You can get the string representation of a C type object with its
__str__() method.

A C type constructor's `is_const` parameter controls whether or not the
_value_ is const. For example:

`const char *`:
    `_PointerCType` containing a const `_ArithCType`.

`const char * const`:
    Const `_PointerCType` containing a const `_ArithCType`.

_CodeGen._ft_c_type() now returns a `_CType` object instead of a string.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoFix: barectf/include/3: remove `byte-order` properties
Philippe Proulx [Tue, 8 Sep 2020 23:52:51 +0000 (19:52 -0400)] 
Fix: barectf/include/3: remove `byte-order` properties

Removed in 4c91e76 ("config.py: remove bit array field type's byte order
property"), so this should not be here.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoFix: size-write-struct-statements.j2: use `ev_type`, not `event_type`
Philippe Proulx [Tue, 8 Sep 2020 20:21:39 +0000 (16:21 -0400)] 
Fix: size-write-struct-statements.j2: use `ev_type`, not `event_type`

3 years agoconfig_parse_v2.py: fix typing issue
Philippe Proulx [Tue, 8 Sep 2020 20:19:02 +0000 (16:19 -0400)] 
config_parse_v2.py: fix typing issue

Reported by mypy 0.782.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agostruct-ft.j2: use a single line for array indices
Philippe Proulx [Tue, 8 Sep 2020 20:15:31 +0000 (16:15 -0400)] 
struct-ft.j2: use a single line for array indices

Still readable and less weird in my opinion.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocgen.py, templates: use new `_CompoundOp` for structure fields
Philippe Proulx [Tue, 8 Sep 2020 20:05:42 +0000 (16:05 -0400)] 
cgen.py, templates: use new `_CompoundOp` for structure fields

This patch adds the `_CompoundOp` class which inherits `_Op`. It also
makes `_AlignOp` and `_WriteOp` inherit a new `_LeadOf` class. The final
operation class hierarchy is:

    _Op
      _CompoundOp
      _LeafOp
        _AlignOp
        _WriteOp

A compound operation is a simple container of suboperations (compound or
leaf). It is expected that a compound field type leads to a compound
operation.

As of this version, the only "real" compound field type is the structure
field type.

`_OpsBuilder` becomes `_OpBuilder` and does not accumulate operations
anymore: it still has a state, but its build_for_root_ft() method
returns a `_CompoundOp` object for a given root structure field type.

The returned compound operation can contain an "align" operation for the
structure field itself as its first suboperation.

Because an `_OpBuilder` doesn't accumulate operations anymore, there's
no need to copy operations anymore.

For a structure field type, _OpBuilder._build_for_ft() returns a single
compound operation having the new serialization and size templates
`serialize-write-struct-statements.j2` and
`size-write-struct-statements.j2`. Those templates contain a C comment
and a simple loop to render the appropriate template for each
suboperation, forwarding the `stream_type` and `ev_type` context
variables.

`barectf.c.j2` is changed to render a single template for any compound
operation. This removes many loops.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agopoetry.lock: update
Philippe Proulx [Tue, 8 Sep 2020 17:05:48 +0000 (13:05 -0400)] 
poetry.lock: update

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoserialize-write-bit-array-statements.j2: use memcpy() when possible
Philippe Proulx [Tue, 8 Sep 2020 16:49:14 +0000 (12:49 -0400)] 
serialize-write-bit-array-statements.j2: use memcpy() when possible

To write a bit array field within a serialization function, you can use
memcpy() directly when all of the following conditions are satisfied:

* The field type's size is 8, 16, 32, or 64.

* The field type's alignment is a multiple of 8.

* The field type's byte order is the target byte order; always the case
  since 4c91e76 ("config.py: remove bit array field type's byte order
  property").

With recent compilers, using bt_bitfield_write_*() vs. memcpy() with
`-O2` gives the same result.

For example, consider this C code:

    const uint8_t *data;

    void with_bitfield(size_t offset, int val)
    {
        bt_bitfield_write_le(&data[offset], 0, sizeof(val) * 8, int, val);
    }

    void with_memcpy(size_t offset, int val)
    {
        memcpy(&data[offset], &val, sizeof(val));
    }

On x86-64, this gets compiled to:

GCC 10.2:
    with_bitfield:
            mov     rax, QWORD PTR data[rip]
            mov     DWORD PTR [rax+rdi], esi
            ret
    with_memcpy:
            mov     rax, QWORD PTR data[rip]
            mov     DWORD PTR [rax+rdi], esi
            ret

Clang 10.0:
    with_bitfield:                          # @with_bitfield
            mov     rax, qword ptr [rip + data]
            mov     dword ptr [rax + rdi], esi
            ret
    with_memcpy:                            # @with_memcpy
            mov     rax, qword ptr [rip + data]
            mov     dword ptr [rax + rdi], esi
            ret

GCC 7.3:
    with_bitfield:
            add     rdi, QWORD PTR data[rip]
            mov     eax, esi
            mov     BYTE PTR [rdi], sil
            mov     BYTE PTR [rdi+1], ah
            sar     esi, 24
            sar     eax, 16
            mov     BYTE PTR [rdi+3], sil
            mov     BYTE PTR [rdi+2], al
            ret
    with_memcpy:
            mov     rax, QWORD PTR data[rip]
            mov     DWORD PTR [rax+rdi], esi
            ret

GCC 4.9.4:
    with_bitfield:
            add     rdi, QWORD PTR data[rip]
            mov     eax, esi
            sar     eax, 8
            mov     BYTE PTR [rdi+1], al
            mov     eax, esi
            mov     BYTE PTR [rdi], sil
            sar     eax, 16
            sar     esi, 24
            mov     BYTE PTR [rdi+2], al
            mov     BYTE PTR [rdi+3], sil
            ret
    with_memcpy:
            mov     rax, QWORD PTR data[rip]
            mov     DWORD PTR [rax+rdi], esi
            ret

Clang 3.0:
    with_bitfield:                          # @with_bitfield
            mov     EAX, ESI
            mov     RCX, QWORD PTR [RIP + data]
            mov     BYTE PTR [RCX + RDI], AL
            mov     BYTE PTR [RCX + RDI + 1], AH  # NOREX
            mov     EDX, EAX
            shr     EDX, 16
            mov     BYTE PTR [RCX + RDI + 2], DL
            shr     EAX, 24
            mov     BYTE PTR [RCX + RDI + 3], AL
            ret

    with_memcpy:                            # @with_memcpy
            mov     RAX, QWORD PTR [RIP + data]
            mov     DWORD PTR [RAX + RDI], ESI
            ret

Because barectf targets embedded and bare metal environments, it's
possible that its user uses an older compiler. Therefore, this patch
changes `serialize-write-bit-array-statements.j2` to prefer memcpy()
when possible as it's very common for memcpy() to get replaced with a
compiler built-in to generate faster code when the copy size is static
(which is always the case for barectf bit array fields).

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoserialize-write-time-statements.j2: capitalize C comment
Philippe Proulx [Tue, 8 Sep 2020 16:48:54 +0000 (12:48 -0400)] 
serialize-write-time-statements.j2: capitalize C comment

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobitfield.h.j2: keep a single version for the target byte order
Philippe Proulx [Tue, 8 Sep 2020 16:37:28 +0000 (12:37 -0400)] 
bitfield.h.j2: keep a single version for the target byte order

Because all bit array fields use the target byte order since 4c91e76
("config.py: remove bit array field type's byte order property"), we
don't need both little-endian and big-endian versions of the
`bitfield.h` macros.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoconfig.py: remove bit array field type's byte order property
Philippe Proulx [Tue, 8 Sep 2020 16:16:05 +0000 (12:16 -0400)] 
config.py: remove bit array field type's byte order property

This patch removes the byte order property from the `_BitArrayFieldType`
class.

Instead, barectf always uses the configuration's target byte order.

As of this version of barectf, where all integral tracing function
parameters are copies, it makes no sense, for example, for a
little-endian CPU to write big-endian data.

The `byte-order` property is also removed in barectf 3 YAML field types.
I'm keeping it in barectf 2 YAML field types for backward compatibility,
but it's ignored now.

From a CTF consumer's perspective, this patch doesn't cause a noticeable
change: the decoded integral value is the same whatever the byte order.

From the barectf API's perspective, it makes the configuration objects
simpler. This patch effectively reverts 7fffc7d ("config: replace trace
type's default BO with configuration's target BO").

It is possible that barectf needs specific bit array field type byte
orders in the future, for example to copy some user payload as is
instead of copying each field; when this time comes, we can reintroduce
the property, adding a way to mark a bit array field type's byte order
as "target" (which would be the default), for example:

    my_ft = barectf.RealFieldType(32,
                                  byte_order=barectf.TARGET_BYTE_ORDER)

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotemplates: rename "licence" -> "license"
Philippe Proulx [Fri, 4 Sep 2020 20:31:03 +0000 (16:31 -0400)] 
templates: rename "licence" -> "license"

Unintentional French.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobitfield.h.j2: restore `bitfield.h`'s original copyright
Philippe Proulx [Fri, 4 Sep 2020 20:28:09 +0000 (16:28 -0400)] 
bitfield.h.j2: restore `bitfield.h`'s original copyright

In e72875e ("templates: commonize the licence header"), I made the
mistake to overwrite `bitfield.h`'s original copyright with mine, so
restore this.

The new `licence-header-footer.j2` template is the bottom part of any
license header.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobitfield.h.j2: remove `_BYTE_ORDER` definition; set relevant macros
Philippe Proulx [Fri, 4 Sep 2020 20:23:07 +0000 (16:23 -0400)] 
bitfield.h.j2: remove `_BYTE_ORDER` definition; set relevant macros

Now that a barectf configuration contains the target byte order, we can
set the relevant macros directly in `bitfield.h.j2`, and therefore
remove the `_BYTE_ORDER` definition.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf: fix Flake8 errors
Philippe Proulx [Fri, 4 Sep 2020 02:03:52 +0000 (22:03 -0400)] 
barectf: fix Flake8 errors

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoconfig_parse_v3.py: fix Pylint errors/warnings
Philippe Proulx [Fri, 4 Sep 2020 01:53:16 +0000 (21:53 -0400)] 
config_parse_v3.py: fix Pylint errors/warnings

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf.c.j2: remove superfluous empty line
Philippe Proulx [Fri, 4 Sep 2020 01:45:31 +0000 (21:45 -0400)] 
barectf.c.j2: remove superfluous empty line

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocgen.py: _CodeGen.gen_src(): remove ugly empty lines before `}`
Philippe Proulx [Fri, 4 Sep 2020 01:41:44 +0000 (21:41 -0400)] 
cgen.py: _CodeGen.gen_src(): remove ugly empty lines before `}`

Jinja 2 makes it hard to have multiple contiguous blocks delimited with
empty lines when using a for loop, while not also having an empty line
at the end.

Therefore, we often get something like this:

    /* Serialize payload */
    {
        /* Align for payload structure */
        _ALIGN(ctx->at, 32);

        /* Write `value` field */
        bt_bitfield_write_le(&ctx->buf[_BITS_TO_BYTES(ctx->at)],
            uint8_t, 0, 32, uint32_t,
            (uint32_t) p_value);
        ctx->at += 32;

    }

This empty line before `}` is really ugly, so use a regex substitution
to fix this.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoconfig: replace trace type's default BO with configuration's target BO
Philippe Proulx [Fri, 4 Sep 2020 01:06:03 +0000 (21:06 -0400)] 
config: replace trace type's default BO with configuration's target BO

This patch removes the `default_byte_order` property from the
`barectf.TraceType` class.

Instead of having this helper, a configuration now has a mandatory
`target_byte_order` property.

Because of the trace type's default byte order role in
`barectf-bitfield.h`, it's more fundamental than a mere "default": it's
actually the byte order you expect the target system to have. In other
words, you could not make the trace type's default byte order big-endian
to when your target system is a little-endian machine: the bit array
serialization process will fail at tracing time. Therefore I found that
the "default byte order" term was misleading.

This change brings a challenge, however: when you don't specify a group
of stream type or trace type features, or when you specify
`barectf.DEFAULT_FIELD_TYPE` for a given feature field type, which byte
order should the automatically created integer field type(s) have?

My solution is to add an optional default byte order parameter to each
constructor which _could_ create an integer field type (recursively). As
soon as this is going to happen, the default byte order must not be
`None`. For example, when you create a `barectf.StreamTypeEventFeatures`
object, you can use `barectf.DEFAULT_FIELD_TYPE`, but you must also pass
a default byte order:

    features = barectf.StreamTypeEventFeatures(barectf.DEFAULT_FIELD_TYPE,
                                               default_byte_order=barectf.ByteOrder.LITTLE_ENDIAN)

This can also be when you create a stream type or a trace type, as if
you don't pass any feature object, then the constructor creates one for
you, which eventually creates default field types. For example:

    trace_type = barectf.TraceType(my_stream_types,
                                   default_byte_order=barectf.ByteOrder.BIG_ENDIAN)

This design is not super elegant, but it's a compromise for the
constructors to have everything they need to create full integer field
types. This is also why the `byte_order` property of a bit array field
type is now mandatory; there's no (peculiar) temporary state where it
remains `None` until the complete trace type exists.

I thought about making the target byte order little-endian by default,
for example, but considering that barectf is used to generate tracers
for embedded and bare-metal systems, I found that solution too
indiscriminate.

In a barectf 3 YAML configuration file:

* The trace type's `$default-byte-order` is removed.

* There's a new, mandatory `target-byte-order` configuration property.

* You can still not specify any bit array field type byte order: the
  parser uses the configuration's target byte order in that case.

A barectf 2 YAML trace type's `byte-order` property (which is already
mandatory) is copied as the v3 configuration node's `target-byte-order`
property.

`bitfield.h.j2` and `metadata.j2` are changed to use the configuration's
target byte order instead of the trace type's default byte order.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoFix: _effective_config_file(): get root node to have the YAML tag
Philippe Proulx [Fri, 4 Sep 2020 01:03:02 +0000 (21:03 -0400)] 
Fix: _effective_config_file(): get root node to have the YAML tag

The `config_node` property of a `config_parse_v3._Parser` object returns
an ordered dict; what we want for _yaml_dump() to write the barectf 3
YAML tag is a `_ConfigNodeV3` which is (now) the
`config_parse_common.root_node` property.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoUse pytest as the testing system instead of Bats
Philippe Proulx [Thu, 3 Sep 2020 20:50:57 +0000 (16:50 -0400)] 
Use pytest as the testing system instead of Bats

pytest has several benefits:

* The tests are written in Python, so I can directly import `barectf`
  and use the package to make barectf operations instead of using the
  CLI.

  Therefore I can test the package itself.

  Also, there's a single Python interpreter instance, so you don't pay
  the Python boot up cost for each test.

* With `pytest-xdist` (also a new dev dependency), you can run tests
  in parallel, for example:

      $ poetry run py.test -v -n4 tests

* It's very well known by the Python community, whereas Bats is not that
  popular.

All in all, this makes the testing process really faster (15 seconds on
my machine; used to be 1m45). It's also somewhat more enjoyable to write
Python code than Bash code to create new tests.

This patch only converts current tests using Bats to pytest.

`tests/config/yaml/conftest.py` defines a pytest fixture named
`config_fail_test`. A test receiving this fixture gets a function which
accepts a `request` fixture as well as a relative path to a YAML file
(from the test file's directory), without the `.yaml` extension. The
testing function uses the barectf API directly to try to create a
configuration from the YAML file, ensuring that
`barectf._ConfigurationParseError` is raised in the process.

`tests/config/yaml/2/test_pass_everything.py` does what
`tests/config/2/pass/everything/pass.bats` used to do, using Python's
`subprocess` module to run the C compiler and `nm` (both of which you
can override with the `CC` and `NM` environment variables).

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf: fix Flake8 errors
Philippe Proulx [Thu, 3 Sep 2020 20:45:06 +0000 (16:45 -0400)] 
barectf: fix Flake8 errors

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoREADME.md: indicate that this README might be incorrect before v3
Philippe Proulx [Thu, 3 Sep 2020 20:38:59 +0000 (16:38 -0400)] 
README.md: indicate that this README might be incorrect before v3

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotox.ini: add `mypy` environment
Philippe Proulx [Thu, 3 Sep 2020 19:10:25 +0000 (15:10 -0400)] 
tox.ini: add `mypy` environment

`pyproject.toml` has the `mypy` dev dependency; tox uses it.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoUse Poetry to manage Pylint dev dependency
Philippe Proulx [Thu, 3 Sep 2020 19:07:49 +0000 (15:07 -0400)] 
Use Poetry to manage Pylint dev dependency

Trying not to have both tox and Poetry manage dev dependencies.

tox always runs `poetry install` and then runs a command installed by
Poetry.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoUse Poetry to manage Flake8 dev dependency
Philippe Proulx [Thu, 3 Sep 2020 19:03:58 +0000 (15:03 -0400)] 
Use Poetry to manage Flake8 dev dependency

Trying not to have both tox and Poetry managing dev dependencies.

tox always runs `poetry install` and then runs a command installed by
Poetry.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agopoetry.lock: update
Philippe Proulx [Thu, 3 Sep 2020 17:23:59 +0000 (13:23 -0400)] 
poetry.lock: update

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocodegen.py, cgen.py: fix Pylint errors/warnings
Philippe Proulx [Thu, 3 Sep 2020 17:19:01 +0000 (13:19 -0400)] 
codegen.py, cgen.py: fix Pylint errors/warnings

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocgen.py: add type hints
Philippe Proulx [Thu, 3 Sep 2020 17:16:42 +0000 (13:16 -0400)] 
cgen.py: add type hints

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocodegen.py: add type hints
Philippe Proulx [Thu, 3 Sep 2020 16:09:18 +0000 (12:09 -0400)] 
codegen.py: add type hints

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoReorganize JSON schemas under `barectf/schemas/config`
Philippe Proulx [Thu, 3 Sep 2020 16:03:35 +0000 (12:03 -0400)] 
Reorganize JSON schemas under `barectf/schemas/config`

Instead of:

    2/
      config/
    3/
      config/
    common/
      config/

Use:

    config/
      2/
      3/
      common/

This requires less inodes.

On a more serious note, barectf might have schemas for other things than
configuration in the future, so all the existing schemas currently
really belong to the configuration domain.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf.c.j2: use single return points
Philippe Proulx [Thu, 3 Sep 2020 15:55:32 +0000 (11:55 -0400)] 
barectf.c.j2: use single return points

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf.c.j2: do not pass useless parameters to _ev_size_*() functions
Philippe Proulx [Thu, 3 Sep 2020 15:48:48 +0000 (11:48 -0400)] 
barectf.c.j2: do not pass useless parameters to _ev_size_*() functions

This patch changes _CodeGen._proto_params_str() in `gen.py` to accept a
`only_dyn` parameter. When `only_dyn` is `True`, then the function only
generates function prototype parameter strings for field types which
have a dynamic size (string field types, as of this version).

This patch also changes the ft_call_params() macro to add an `only_dyn`
parameter which serves the same purpose.

The purpose of this is not calling the _ev_size_*() functions with
parameters which correspond to statically-sized field types.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years ago`_FieldType`: add `size_is_dynamic` property
Philippe Proulx [Thu, 3 Sep 2020 15:47:33 +0000 (11:47 -0400)] 
`_FieldType`: add `size_is_dynamic` property

This new property indicates if the field type's size is static or
dynamic.

A string field type has a dynamic size, as well as any structure field
type which contains one, recursively. As of this version, all other
field types have static sizes.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotemplates: capitalize C comments
Philippe Proulx [Thu, 3 Sep 2020 15:34:19 +0000 (11:34 -0400)] 
templates: capitalize C comments

This is just plus beau à mon avis.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoPut C code generator in its own module
Philippe Proulx [Thu, 3 Sep 2020 15:28:07 +0000 (11:28 -0400)] 
Put C code generator in its own module

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotemplates: use Jinja 2's `true`, not Python's `True`
Philippe Proulx [Thu, 3 Sep 2020 15:23:29 +0000 (11:23 -0400)] 
templates: use Jinja 2's `true`, not Python's `True`

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoMake generated C code as `const` as possible
Philippe Proulx [Thu, 3 Sep 2020 15:19:36 +0000 (11:19 -0400)] 
Make generated C code as `const` as possible

This patch changes `gen.py` and many C-generating templates to make
barectf generate C code with as many `const` variables as possible.

The Jinja 2 `ft_c_type` filter now accepts a parameter to make the
returned C type `const`.

All the `*_params_str` filters also do; the reason is that I don't want
the public header to show those useless `const` parameters (for
variables), but I want the same parameters to be `const` in the C source
file.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf.c.j2: _ALIGN(): make it explicit that `_at_var` is a var. name
Philippe Proulx [Thu, 3 Sep 2020 14:39:02 +0000 (10:39 -0400)] 
barectf.c.j2: _ALIGN(): make it explicit that `_at_var` is a var. name

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agogen.py, templates: use root FT prefixes which match their name
Philippe Proulx [Thu, 3 Sep 2020 14:36:45 +0000 (10:36 -0400)] 
gen.py, templates: use root FT prefixes which match their name

The new prefixes match the new nerminology:

`ph`: Packet header
`pc`: Packet context
`eh`: Event header
`ecc`: Event common context
`sc`: Specific context
`p`: Payload

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agometadata.j2: root_ft(): do not indent within macro
Philippe Proulx [Thu, 3 Sep 2020 14:29:53 +0000 (10:29 -0400)] 
metadata.j2: root_ft(): do not indent within macro

The root_ft() macro itself does not know its rendering context.
Therefore, apply the indentation filter to macro expansion sites
instead.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf.c.j2: packet closing function: do not repeat member names
Philippe Proulx [Thu, 3 Sep 2020 14:25:56 +0000 (10:25 -0400)] 
barectf.c.j2: packet closing function: do not repeat member names

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobitfield.h.j2: do not prefix bitfield functions
Philippe Proulx [Thu, 3 Sep 2020 14:22:02 +0000 (10:22 -0400)] 
bitfield.h.j2: do not prefix bitfield functions

The `barectf-bitfield.h` file is only included by `barectf.c` now, so
having such prefixes is pointless.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotemplates: commonize the licence header
Philippe Proulx [Thu, 3 Sep 2020 14:17:01 +0000 (10:17 -0400)] 
templates: commonize the licence header

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotemplates: add licence headers to all templates
Philippe Proulx [Thu, 3 Sep 2020 14:11:55 +0000 (10:11 -0400)] 
templates: add licence headers to all templates

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf/templates/metadata/*.j2: normalize
Philippe Proulx [Thu, 3 Sep 2020 04:32:20 +0000 (00:32 -0400)] 
barectf/templates/metadata/*.j2: normalize

* Indent control structures.
* Do not use whitespace control when not needed.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoFix: _IntegerFieldType.__init__(): use correct alignment
Philippe Proulx [Thu, 3 Sep 2020 01:23:56 +0000 (21:23 -0400)] 
Fix: _IntegerFieldType.__init__(): use correct alignment

This patch makes _IntegerFieldType.__init__() consider `alignment` if
it's not `None` instead of completely ignoring it.

😒

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years ago_CCodeGenerator.generate_c_src(): use Jinja 2 templates
Philippe Proulx [Fri, 28 Aug 2020 22:00:56 +0000 (18:00 -0400)] 
_CCodeGenerator.generate_c_src(): use Jinja 2 templates

This patch makes _CCodeGenerator.generate_c_src() use Jinja 2 templates
instead of a generic code generator.

The purpose of switching to Jinja 2 templates is to make such templates
more readable than Python code, to make them reusable, and to assign
different concerns to different templates.

As such a change is hard to make incrementally, this patch changes many
parts of the tree at once. More specifically, the impacted parts are:

`gen.py`:
    * _CCodeGenerator.generate_c_src() now creates _all_ the operations
      (which used to be named "serialization actions"; more about this
      below) for all the root field types and passes this to the
      `barectf.c.j2` template at rendering time.

      _CCodeGenerator.generate_c_src() doesn't use anything from
      `barectf.codegen` or `barectf.templates`.

    * What used to be named `_SerializationActions` is now named
      `_OpsBuilder`.

      An `_OpsBuilder` object does pretty much the same job as what a
      `_SerializationActions` object previously did: when you call its
      append_root_ft() method, it iterates the members of the structure
      field type recursively to append corresponding operations to
      itself. You can then get its current list of operations with its
      `ops` property.

    * An operation is either "align" (`_AlignOp`) or "write"
      (`_WriteOp`).

      Each of those operations can occur in two different contexts:
      serialization or size computation. Therefore, each operation
      contains two `barectf.template._Template` objects: one for the
      serialization function and one for the size computation function.
      You can render both templates using the serialize_str() and
      size_str() methods (to which you can pass more rendering context).

      Although most operations use the same generic templates, the
      serialization template of some "write" operations can be custom.
      This is how special fields are handled, like the packet header's
      magic number or the event header's time.

      In `barectf/templates/c`, template files are named as such:

      +--------------------+------------------------+-------------------+
      | Operation/Function | Serialization          | Size              |
      +====================+========================+===================+
      | Align              | `serialize-align-*.j2` | `size-align-*.j2` |
      | Write              | `serialize-write-*.j2` | `size-write-*.j2` |
      +--------------------+------------------------+-------------------+

    * An operation contains all the names you need to craft a source
      variable name (to be joined with `_`).

      This is why root field type prefixes (`_RootFtPrefixes`) don't
      contain a trailing underscore anymore.

      The topmost name of an operation `o` is `o.top_name`; templates
      mostly use this property for C comments.

`template.py`:
`codegen.py`:
    Both files are removed as they're no longer needed by the project.

`templates/c`:
    `barectf.c.j2`:
        New template which generates the whole `barectf.c` file (given
        the file name prefix is `barectf`).

        This template generates, in this order:

        * The licence header.
        * Utility C macros.
        * Internal data structures.
        * Public barectf context access funtions.
        * Internal functions.
        * Public barectf context initialization function.
        * For each stream type:
          * Public packet opening function.
          * Public packet closing function.
          * Internal event header serialization function.
          * Internal event common context serialization function.
          * For each event type:
            * Internal serialization function.
          * For each event type:
            * Internal size computation function.
          * For each event type:
            * Public tracing function.

    `barectf.c-macros.j2`:
        Macros to be used by the `barectf.c.j2` template:

        * open_close_func_preamble()
        * ft_call_params()

    `common.j2`:
        New trace_func_name() and op_src() macros.

    `align-statements-comment.j2`:
        C comment for any alignment statement.

    `serialize-align-statements.j2`:
        Alignment statements for serialization functions.

    `serialize-write-*statements.j2`:
        Writing statements for serialization functions.

    `serialize-write-statements-comment.j2`:
        C comment for generic writing statements (for serialization
        functions).

    `size-align-statements.j2`:
        Alignment statements for size computation functions.

    `size-write-*-statements.j2`:
        Writing statements for size computation functions.

    Also, to make templates simpler, I standardized the following C
    variable names:

    `ctx`:
        Generic barectf context.

    `sctx`:
        Stream-type-specific barectf context.

    `vctx`:
        `ctx` as a `void` pointer.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotsdl182gen.py: remove unused `jinja2` import
Philippe Proulx [Fri, 28 Aug 2020 14:45:05 +0000 (10:45 -0400)] 
tsdl182gen.py: remove unused `jinja2` import

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years ago_CCodeGenerator.generate_header(): use Jinja 2 templates
Philippe Proulx [Thu, 27 Aug 2020 19:51:52 +0000 (15:51 -0400)] 
_CCodeGenerator.generate_header(): use Jinja 2 templates

This patch makes _CCodeGenerator.generate_header() generate the main
barectf header file using Jinja 2 templates.

The master template, `barectf.h.j2`, includes:

`c-common.j2`:
    Common macros for C templates.

`c-ctx-init-func-proto.j2`:
    Context initialization function prototype.

`c-open-func-proto.j2`:
    Packet opening function prototype.

`c-close-func-proto.j2`:
    Packet closing function prototype.

`c-trace-func-proto.j2`:
    Tracing function prototype.

This patch removes a lot of code from `gen.py` and `templates.py` which
now has its equivalent in Jinja 2 templates.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoAdd `common.j2` template
Philippe Proulx [Thu, 27 Aug 2020 17:37:30 +0000 (13:37 -0400)] 
Add `common.j2` template

This file can contain common variables and macros.

It is to be imported by other templates, for example by `bitfield.h.j2`
in this patch.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoJinja 2 templates: stylize block comments like C block comments
Philippe Proulx [Thu, 27 Aug 2020 17:37:00 +0000 (13:37 -0400)] 
Jinja 2 templates: stylize block comments like C block comments

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf.h: do not include `barectf-bitfield.h`
Philippe Proulx [Thu, 27 Aug 2020 15:59:21 +0000 (11:59 -0400)] 
barectf.h: do not include `barectf-bitfield.h`

The public header itself doesn't need the macros of
`barectf-bitfield.h`; it only pollutes the global namespace with
internal macro names.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobitfield.h.j2: prefix `CAST_PTR` with the uppercase identifier prefix
Philippe Proulx [Thu, 27 Aug 2020 15:45:30 +0000 (11:45 -0400)] 
bitfield.h.j2: prefix `CAST_PTR` with the uppercase identifier prefix

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years ago_CCodeGenerator.generate_bitfield_header(): use a Jinja 2 template
Philippe Proulx [Thu, 27 Aug 2020 15:35:47 +0000 (11:35 -0400)] 
_CCodeGenerator.generate_bitfield_header(): use a Jinja 2 template

Benefits:

* `\` instead of `\\` in the template.

* Jinja 2 variables instead of `$PREFIX`, `$prefix$`, etc. which makes
  it possible to have template syntax highlighting for example.

* `LITTLE_ENDIAN` or `BIG_ENDIAN` generated by the template using the
  barectf configuration.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agogen.py: add _CCodeGenerator._create_{file_}template() methods
Philippe Proulx [Thu, 27 Aug 2020 15:34:40 +0000 (11:34 -0400)] 
gen.py: add _CCodeGenerator._create_{file_}template() methods

Those new methods create a template (or a file template) using the C
code generator's barectf configuration.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotemplate.py: define a `_Template` class instead of two functions
Philippe Proulx [Thu, 27 Aug 2020 15:11:16 +0000 (11:11 -0400)] 
template.py: define a `_Template` class instead of two functions

This patch changes `template.py` so as to offer the `_Template` class
instead of the _create_template() and _render_template() functions.

This makes it possible to move the `is_file_template` parameter from
_render_template() to the template's constructor, as this really is a
template-specific option, not a rendering-specific option.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoUse Jinja 2 templates to generate the `metadata` file
Philippe Proulx [Thu, 20 Aug 2020 19:52:48 +0000 (15:52 -0400)] 
Use Jinja 2 templates to generate the `metadata` file

The goal of this patch is to, as much as possible, and as long as
everything remains as readable as possible, move any `metadata` file
templating outside of pure Python code to improve template readability,
maintenability, and reuse.

This patch does the following:

1. Adds a Jinja 2 dependency to `pyproject.toml`.

2. Adds the `template` module which contains helpers to create and
   render a Jinja 2 template.

   The _create_template() function uses a `jinja2.PackageLoader` object
   to load a named package from the `barectf` packet's `templates`
   directory.

   It creates an environment with common parameters and common
   filters:

   `indent_tab`:
       Like Jinja 2's built-in `indent`, but uses tabs instead of
       spaces.

   `escape_sq`:
       Escapes a double-quoted string (backslashes and double quotes).

   Jinja 2 templates can expect to have access to the `barectf_config`
   and `barectf_version` modules, as well as, possibly, the current
   barectf configuration as the `cfg` variable.

   The _render_template() function renders a given template with a given
   context, keeping a single newline at the end if `is_file_template` is
   `True`.

3. Adds Jinja 2 templates to generate the `metadata` file:

   `metadata.j2`:
       Top-level template for the whole file.

   `metadata-enum-ft.j2`:
       Enumeration field type block template.

   `metadata-int-ft.j2`:
       Integer field type block template.

   `metadata-real-ft.j2`:
       Real field type block template.

   `metadata-str-ft.j2`:
       String field type block template.

   `metadata-struct-ft.j2`:
       Structure field type block template.

4. Changes `barectf/tsdl182gen.py` so as to use 2. with 3.

   `barectf/tsdl182gen.py` defines custom Jinja 2 filters to help the
   templates of 3.:

   `bo_str`:
       Converts a `barectf.ByteOrder` value to the `le` or `be` string.

   `disp_base_int`:
       Converts a `barectf.DisplayBase` value to an equivalent integer.

   `int_ft_str`:
       Converts an integer field type object to its TSDL string
       equivalent.

       The first line is not indented.

   `ft_str`:
       Converts any field type object to its TSDL string equivalent.

       The first line is not indented.

5. Adds the `barectf.TraceType.clock_types` property to easily access
   the set of required clock types (by all stream types).

   This is required by 3.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli.py: fix Pylint errors
Philippe Proulx [Tue, 11 Aug 2020 20:27:54 +0000 (16:27 -0400)] 
cli.py: fix Pylint errors

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoAdd Python type hints
Philippe Proulx [Tue, 11 Aug 2020 18:33:35 +0000 (14:33 -0400)] 
Add Python type hints

This patch adds Python type hints to all the modules except
`codegen.py`, `gen.py`, and `tsdl182gen.py`, as it is likely that those
will change significantly in the future.

Mypy 0.782 reports no errors with this patch.

The few errors that were found during the type hint introduction process
are fixed as part of this patch.

`typing.py` is a new module which contains public and private type
aliases, mostly derivatives of `int` to add semantics (index, count,
version number, and the rest). The ones that are public are available
from the `barectf` package itself (`__init__.py`). A `barectf` API user
doesn't need to use them without static type checking needs. If she
wants to, then she must use `barectf` types explicitly, for example:

    import barectf

    clk_type = barectf.ClockType('my_clock',
                                 frequency=barectf.Count(100000))

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agov3 YAML config: require `$default-byte-order` property
Philippe Proulx [Mon, 10 Aug 2020 20:44:36 +0000 (16:44 -0400)] 
v3 YAML config: require `$default-byte-order` property

CTF 1.8 consumers require the `trace` block's `byte_order` property, so
let's require it in barectf too for the moment.

We can relax this requirement later if possible.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years ago__init__.py: remove `barectf_config_file` name
Philippe Proulx [Sat, 8 Aug 2020 01:37:55 +0000 (21:37 -0400)] 
__init__.py: remove `barectf_config_file` name

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli.py: fix wrong comment
Philippe Proulx [Sat, 8 Aug 2020 01:00:52 +0000 (21:00 -0400)] 
cli.py: fix wrong comment

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli: add short command descriptions to their help text
Philippe Proulx [Fri, 7 Aug 2020 21:39:55 +0000 (17:39 -0400)] 
cli: add short command descriptions to their help text

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli: add `--help` usage and description in command help texts
Philippe Proulx [Fri, 7 Aug 2020 21:35:56 +0000 (17:35 -0400)] 
cli: add `--help` usage and description in command help texts

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli: add `show-configuration-version` command
Philippe Proulx [Fri, 7 Aug 2020 21:32:24 +0000 (17:32 -0400)] 
cli: add `show-configuration-version` command

This new command prints either `2` or `3` on a single line depending on
the major version of the given configuration file, for example:

    $ barectf show-configuration-version my-config.yaml
    2

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli: add `show-effective-configuration` command
Philippe Proulx [Fri, 7 Aug 2020 21:17:13 +0000 (17:17 -0400)] 
cli: add `show-effective-configuration` command

This new command is equivalent to `generate --dump-config`, but in a
more dedicated way:

    $ barectf show-effective-configuration config.yaml

It only accepts relevant options.

One of the options is `--indent-spaces` which controls the indentation
space count of printed YAML lines.

This patch deprecates `--dump-config`: I'll leave it working in
barectf 3 to remain backward-compatible, but I'm removing it from the
`generate` command's help.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli: introduce Git-like commands
Philippe Proulx [Fri, 7 Aug 2020 19:21:15 +0000 (15:21 -0400)] 
cli: introduce Git-like commands

This patch makes the barectf CLI work with commands like Git:

    $ barectf COMMAND COMMAND-ARGS

The goal of this change is to have the `barectf` tool perform more than
tracer generation in the future. For instance, the current
`--dump-config` option should be a dedicated command instead of being
part of the generation process.

As of this patch, the only available command is `generate` (with a `gen`
alias) which does exactly what `barectf` does without an explicit
command:

    $ barectf generate config.yaml
    $ barectf config.yaml

`--help` and `--version` are "general" options; you need to put them
before the command name, if any:

    $ barectf --help
    $ barectf --version

You can also put `--help` after the command name to get this command's
help:

    $ barectf generate --help

`argpar.py` is a Python equivalent of the low-level parts of
<https://github.com/efficios/argpar> which I originally wrote for
Babeltrace 2.

While I acknowledge that it's ludicrous to write a custom argument
parser in Python considering that the standard library and PyPI packages
offer many of them, I couldn't find one which can satisfy the "default
command" use case, with this default command accepting a non-option
argument. For:

    $ barectf config.yaml

all of them indicate that `config.yaml` is not a valid command name.

Of course this is because there's ambiguity when your configuration file
happens to be named `generate` in the current working directory:

    $ barectf generate

This can also be the `generate` command with a missing configuration
file path. As of this patch, this is what happens: `barectf` prefers the
command name. It is such a corner case that I'm not spending ONE MINUTE
on it. That being said, there's a workaround:

    $ barectf ./generate

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoFix: add missing `tests/config/2/fail/metadata` directory
Philippe Proulx [Thu, 30 Jul 2020 14:43:43 +0000 (10:43 -0400)] 
Fix: add missing `tests/config/2/fail/metadata` directory

Was ignored because `tests/config/.gitignore` ignores `metadata` files
(and directories).

Unignore `tests/config/2/fail/metadata` in `tests/config/.gitignore`.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoFix: remove import cycles from `config.py`
Philippe Proulx [Thu, 30 Jul 2020 13:56:29 +0000 (09:56 -0400)] 
Fix: remove import cycles from `config.py`

Now `config.py` contains the configuration objects and `config_file.py`
the configuration file functions.

This made barectf fail on Python 3.6.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoUse barectf.cli._run() as the CLI's starting function
Philippe Proulx [Wed, 29 Jul 2020 14:49:51 +0000 (10:49 -0400)] 
Use barectf.cli._run() as the CLI's starting function

I don't want to expose barectf.cli.run().

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoAdd package inclusion dir. at the API level
Philippe Proulx [Wed, 29 Jul 2020 14:31:30 +0000 (10:31 -0400)] 
Add package inclusion dir. at the API level

This patch adds a `with_package_inclusion_directory` boolean parameter
(true by default) to the barectf.effective_configuration_file() and
barectf.configuration_from_file() functions.

If this new parameter is true, then the package inclusion directory
(`barectf/include/2` or `barectf/include/3`, depending on the
configuration file's version) is appended to the provided inclusion
directories.

This patch moves this logic from the CLI module to the configuration
modules. It is my understanding that users would want to be able to
include package YAML files using only the API. In `cli.py`,
_parse_args() now simply appends the current working directory, so that
the order stays the same:

1. `--include-dir` options, in order.
2. Current working directory.
3. Package inclusion directory.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotox.ini: keep a single `pylint` environment (warnings + errors)
Philippe Proulx [Mon, 27 Jul 2020 21:00:01 +0000 (17:00 -0400)] 
tox.ini: keep a single `pylint` environment (warnings + errors)

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agotox.ini: rename `pep8` environment to `flake8`
Philippe Proulx [Mon, 27 Jul 2020 20:59:39 +0000 (16:59 -0400)] 
tox.ini: rename `pep8` environment to `flake8`

That's the tool's name.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoIntroduce new barectf configuration API and YAML configuration schema
Philippe Proulx [Mon, 27 Jul 2020 19:38:11 +0000 (15:38 -0400)] 
Introduce new barectf configuration API and YAML configuration schema

This patch:

* Updates the configuration API (breaking change) to:

  * Match, more or less, Babeltrace 2's terminology.
  * Be more clear and consistent.
  * Prepare for CTF 2.

* Adds support for a new type of YAML configuration to match the updated
  configuration API.

  Internally, the new schema is named "v3" as it will be barectf 3's
  configuration format. The (now) old schema is named "v2".

* Updates all the existing code to deal with the updated configuration
  API.

I admit that this is a very big change, but it was too arduous to split
in various patches. This message explains what changes and why.

Configuration API changes (metadata)
====================================
barectf 2's metadata API has existed for five years now.

During those five years, I had the chance to work on Babeltrace 2,
CTF 2, and LTTng, and polished CTF's concepts, type relations, and
terminology.

The main goal of this patch is, as much as possible, to make barectf's
configuration API match what was done in Babeltrace 2's trace IR API [1]
at the metadata level.

A few examples:

* All metadata objects are "types" (classes in Babeltrace 2): field
  type, clock type, event type, stream type, and so on.

* A signed enumeration field type inherits an enumeration field type
  (which inherits an integer field type) and a signed integer field
  type.

* Integer and string field types have no encoding property.

  If we need them, we can introduce text array field types in the
  future.

  A string field type has a fixed UTF-8 (a superset of ASCII) encoding.

With this patch, an integer field type doesn't have any property
mapping. Instead, when you create a stream type, you can specify a
default clock type: the event time and packet beginning/end time members
refer to this default clock type in the generated TSDL metadata stream
so that each stream has an instance of it.

One other important change is that, with this patch, you cannot specify:

* The packet header field type of a trace type.
* The packet context field type of a stream type.
* The event header field type of a stream type.

I believe it was an error to let the user control those structure field
types as, fundamentally, they are "format" field types (reserved by
CTF).

As of this patch, you can enable/disable trace type and stream type
features which eventually translate to the automatic creation of the
field types listed above. For example, the "discarded events counter
field type" feature of a stream type controls whether or not the packets
of its instances (streams) contain a discarded events counter field. You
can either:

* Disable the feature (`None`).

* Enable the feature with a default field type (`DEFAULT_FIELD_TYPE`).

* Enable the feature with a custom field type
  (`UnsignedIntegerFieldType` instance).

The packet context field type is a bit of an exception here as it can
contain both format (total size, content size, beginning/end times, and
the rest) and user members. As such, when you create a stream type, you
can specify extra packet context field type members.

This feature approach hides all the reserved CTF member names (`id`,
`magic`, `uuid`, `timestamp_begin`, and the rest): barectf deals with
this internally. There are sane defaults, for example:

* The "magic" and "stream type ID" trace type features are enabled by
  default.

* If, when you create a stream type, you specify a default clock type
  and default features, then the event time and packet beginning/end
  times features are enabled.

All the configuration names are in `config.py` (removing `metadata.py`).

The big picture is:

* A configuration has a trace and options (prefixes and other generation
  options).

* A trace has an environment (think TSDL environment) and a type.

* A trace type has a default byte order, a UUID, features, and one or
  more stream types.

* A stream type has an ID, a name, an default clock type,
  features, extra packet context field type members, a common event
  context field type, and one or more event types.

* A clock type has a name, a frequency, a UUID, a description, a
  precision, an offset, and whether or not its origin is the Unix epoch.

* An event type has an ID, a name, a log level, and specific context and
  payload field types.

* A field type (abstract) has an alignment.

* A bit array field type (abstract) adds the size (bits) and byte order
  properties to a field type.

* An integer field type adds the preferred display base properties to a
  bit array field type.

* An enumeration field type adds the mappings property to an integer
  field type.

* A real field type is a bit array field type.

  The only valid sizes are 32 (single-precision) and 64
  (double-precision).

* An array field type (abstract) adds element field type property to a
  field type.

* A static array field type adds the length property to an array field
  type.

  This field type is only used for the "UUID field type" trace type
  feature.

* A structure field type has named members.

  Each member has a field type.

  In the future (CTF 2), a structure field type member could also
  contain custom user attributes.

New YAML configuration schema
=============================
The barectf 3 YAML configuration format mirrors the new configuration
API, with some sugar to make it easier to write a YAML configuration
file.

The most significant changes are:

* There's no configuration version property.

  Starting from barectf 3, a barectf YAML configuration node is a map
  with the YAML tag [2] `tag:barectf.org,2020/3/config`. Consequently, a
  barectf 3 YAML configuration file will typically start like this:

      %YAML 1.2
      --- !<tag:barectf.org,2020/3/config>

  As you can see, the barectf major version is encoded within the tag.
  The major version is enough: minor bumps of barectf 3 will only add
  features without breaking the schema.

  Only the configuration node needs an explicit tag: all the nodes under
  it have implicit tags according to the schema. This top-level tag
  makes it possible to integrate a barectf 3 configuration node within
  another YAML document, if this ever becomes a use case.

* The terminology, the property names, and, in general, the object
  layouts, are the same as with the configuration API.

* There are two ways to specify prefixes:

  Single prefix:
      Use `PREFIX_` for C identifiers, where `PREFIX` is the single
      prefix, and `PREFIX` for file names.

      For example, with

          prefix: hello

      identifiers start with `hello_` and file names with `hello`.

  Explicit prefixes:
      Specify the exact prefixes to be used for C identifiers and file
      names.

      For example, with

          prefix:
            identifier: lol_
            file-name: meow

      identifiers start with `lol_` and file names with `meow`.

* For the trace type and stream types, specify features in the
  `$features` property.

  Example:

      $features:
        magic-field-type: true
        uuid-field-type: false
        stream-type-id-field-type:
          class: unsigned-int
          size: 8
          byte-order: le

  A field type feature can be one of:

  True:
      Use the default field type.

  False:
      Disable the feature.

  Field type node:
      Use a specific field type.

* The only way to make a stream type the default stream type is with its
  `$is-default` property.

  Example:

      my_stream:
        $is-default: true

  Only one stream type can have this property set to true.

* Specify field type aliases with any order, whatever the dependencies.

  Example:

      $field-type-aliases:
        hello:
          $inherit: meow
          alignment: 32
        meow:
          class: signed-int
          size: 32

* An enumeration field type is an integer field type; there's no concept
  of "value field type" anymore.

  Example:

      class: unsigned-enum
      size: 32
      byte-order: le
      mappings:
        # ...

* An enumeration field type's `mappings` property is a map of labels to
  lists of integer ranges.

  The order of individual mappings is not important.

  An integer range is either a single integer or a pair of integers
  (lower and upper).

  Example:

      class: unsigned-enum
      size: 32
      mappings:
        HOLA: [2]
        MEOW:
          - 23
          - [4, 17]
        ROCK:
          - [1000, 2000]
          - [3000, 4000]
          - [5000, 6000]

* A real field type is a bit array field type.

  Example (single-precision):

      class: real
      size: 32

* Specify the members of a structure field type like a YAML ordered
  map [3], without having to specify the `tag:yaml.org,2002:omap`
  tag.

  This fixes a "bug" which exists since barectf 2.0: a YAML map is not
  ordered per se; as such, the members of a structure field type cannot
  be a simple map. It works in barectf 2 because PyYAML parses the map
  entries in order, but there's no requirement to do so.

  Example:

      class: struct
      members:
        - sup:
            field-type: uint32
        - meow:
            field-type:
              class: string
        - bob:
            field-type:
              $inherit: double
              byte-order: be

  It might seem silly to specify `field-type` for each member, but CTF 2
  might bring user attributes for individual structure field type
  members, so barectf 3 will be ready. For example:

      class: struct
      members:
        - sup:
            field-type: uint32
        - meow:
            field-type:
              class: string
            user-attributes:
              'https://example.com/ns/ctf':
                type: user-name
                color: blue
        - bob:
            field-type:
              $inherit: double
              byte-order: be

Internally, barectf detects the YAML configuration file's version. If
it's a barectf 2 configuration node, it converts it to a barectf 3
configuration node, and then it decodes it, as such:

    barectf 2 YAML    barectf 3 YAML
    config. node   -> config node.   -> barectf config. object (API)

What you get with `--dump-config` occurs after step 1.

barectf 3 will remain fully backward compatible regarding barectf 2 YAML
configurations and the CLI. This is why the `barectf/include` directory
contains inclusion files for both barectf 2 and 3: the CLI selects the
appropriate directory based on the detected version.

`barectf/schemas` also contains JSON schemas for both versions of the
configuration, as well as common schemas.

Configuration API changes (functions)
=====================================
The configuration API functions are now:

configuration_file_major_version():
    Returns the major version (2 or 3) of a YAML configuration file-like
    object.

effective_configuration_file():
    Returns the effective configuration file (YAML string) using a
    file-like object and a list of inclusion directories.

    An effective configuration file is a file where:

    * All field type nodes are expanded (aliases and inheritance).

    * Log level aliases are substituted for their integral equivalents.

    * Some properties are normalized.

      For example, the `be` byte order becomes `big-endian` (both are
      valid). Also, null properties are simply removed.

configuration_from_file():
    Returns a `barectf.Configuration` object from a YAML configuration
    file-like object.

Code generation API
===================
This patch also brings changes to the code generation API.

Now you create a `barectf.CodeGenerator` object, passing a barectf
configuration.

With such a code generator object, call its generate_c_headers(),
generate_c_sources(), and generate_metadata_stream() methods to generate
code.

generate_c_headers() and generate_c_sources() return lists of
`barectf.gen._GeneratedFile` objects. Such an object contains the name
of the file and its (UTF-8 text) contents.

generate_metadata_stream() returns a single `barectf.gen._GeneratedFile`
object.

`__init__.py`
=============
The whole barectf API is now available directly under its package.

`__init__.py` picks what needs to be exposed from its different modules.

Tests
=====
Tests which were directly under `tests/config` are moved to
`tests/config/2` as we could add barectf 3 configuration tests in the
future.

[1]: https://babeltrace.org/docs/v2.0/libbabeltrace2/group__api-tir.html
[2]: https://yaml.org/spec/1.2/spec.html#id2761292

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agopoetry.lock: update
Philippe Proulx [Mon, 27 Jul 2020 19:37:08 +0000 (15:37 -0400)] 
poetry.lock: update

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoRename `msg`/`ctx` properties 👉 `message`/`context`
Philippe Proulx [Mon, 1 Jun 2020 15:17:27 +0000 (11:17 -0400)] 
Rename `msg`/`ctx` properties 👉 `message`/`context`

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agobarectf: use package names (do not import "from")
Philippe Proulx [Fri, 29 May 2020 22:12:19 +0000 (18:12 -0400)] 
barectf: use package names (do not import "from")

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli.py: fix PEP 8 errors, as reported by `flake8`
Philippe Proulx [Fri, 29 May 2020 21:58:26 +0000 (17:58 -0400)] 
cli.py: fix PEP 8 errors, as reported by `flake8`

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli.py: format
Philippe Proulx [Fri, 29 May 2020 21:56:49 +0000 (17:56 -0400)] 
cli.py: format

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli.py: standardize `exc` as exception name
Philippe Proulx [Fri, 29 May 2020 21:56:00 +0000 (17:56 -0400)] 
cli.py: standardize `exc` as exception name

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agocli.py: replace `'...'.format(...)` with f-strings
Philippe Proulx [Fri, 29 May 2020 21:54:05 +0000 (17:54 -0400)] 
cli.py: replace `'...'.format(...)` with f-strings

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agoconfig_parse.py: replace `'...'.format()` with f-strings
Philippe Proulx [Fri, 29 May 2020 20:53:13 +0000 (16:53 -0400)] 
config_parse.py: replace `'...'.format()` with f-strings

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
3 years agopyproject.toml: require Python 3.6+
Philippe Proulx [Fri, 29 May 2020 20:52:21 +0000 (16:52 -0400)] 
pyproject.toml: require Python 3.6+

I want to use f-strings.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
This page took 0.050547 seconds and 4 git commands to generate.