Fix: Handle barectf 2 configurations with any configured byte order
authorErica Bugden <ebugden@efficios.com>
Fri, 5 May 2023 15:19:32 +0000 (11:19 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Tue, 16 May 2023 19:18:07 +0000 (15:18 -0400)
commita209cf4d87e8b6bdfdff1b54d0b59a821cff6eef
tree9748d1f482333b79c2a3375fb1f0ab1476dc0b77
parent2feb59590ab57229b4e8fddf03ed7ff9ff27438f
Fix: Handle barectf 2 configurations with any configured byte order

Context:

In barectf 2 (v2), the ‘byte-order’ property allows users to configure
the byte order with which traces are written (completely independently
of the traced architecture’s native byte order).

In barectf 3 (v3), the v2 property ‘byte-order’ was removed and the v3
property ‘native-byte-order’ is used instead. Currently, barectf 3
requires that the configured trace byte order match the byte order of
the traced architecture (i.e. the native byte order). This new
requirement promotes efficient tracing as it allows memcpy() to be used
for trace writing. Conversely, writing the trace in a different byte
order as the traced architecture requires at least one additional CPU
operation (byte swap) per written data point.

Issue:

Because of this use of memcpy() in trace writing (see implementation
section below), barectf 3 does not correctly handle barectf 2
configurations where the tracer's configured byte order does not match
the system's native byte order.

For example, consider a barectf 2 configuration file configured to
produce traces with a little-endian byte order
(byte-order: little-endian). If a barectf 3 tracer generated with this
configuration is used on a big-endian system, the traces produced will
appear corrupted.

Because the barectf 3 tracer copies data as-is with memcpy() (without
checking or swapping byte order), the byte order of the trace data
produced will not match the expected byte order (described in the
trace's metadata file) so the trace reader (e.g. babeltrace) will not be
able to read it.

Implementation:

The difference between barectf 2 (v2.3.1) and 3 byte order behavior:

 * barectf 2: Always uses the bt_bitfield_write functions when writing
    trace data.
 * barectf 3: Typically uses memcpy() when writing trace data (unless
    the data is not byte-aligned or has a length which is not a multiple
    of 8 bits)

In practice, the bt_bitfield_write functions are defined identically in
barectf 2 and barectf 3.

    Note: Although, barectf 2 does define the bt_bitfield_write
    functions with different type arguments depending on the configured
    byte order, in practice, these functions are _always_ called with
    the type unsigned char. So, there is no functional difference
    between how bt_bitfield_write_le and bt_bitfield_write_be are called
    regardless of the configured byte order. This means that we can get
    the same barectf 2 byte order behaviour in barectf 3 without
    adjusting the bt_bitfield_write functions based on the
    configuration.

Changes:

Handle barectf 2 configurations with any configured byte order by adding
an optional property ‘trace-byte-order’ (equivalent of the barectf 2
‘byte-order’ property). When this property is set, the tracer will
write the trace data in the configured byte order regardless of the
traced architecture’s native byte order (like barectf 2).

These changes are transparent for existing users of the barectf Python
library and no changes to their code are required.

The configuration property ‘trace-byte-order’ is implemented with a new
trace type class ‘TraceTypeWithUnknownNativeByteOrder’ both to avoid
breaking changes to the existing ‘TraceType’ class and to encourage use
of the ‘TraceType’ class over the new trace type.

The ‘trace-byte-order’ property is only intended to allow compatibility
with barectf 2 configurations. Because tracers configurated with
‘trace-byte-order’ will always use the bt_bitfield_write functions,
intentional use of this property in barectf 3 is unrecommended as it
could result in a less efficient tracer if the C compiler used is not
good at optimizing.

    Note: With a C compiler that optimizes well, the bt_bitfield_write
    functions will optimize down to either:
        a. 1 store instruction (equivalent to memcpy()), or
        b. 1 byte swap and 1 store (if the config byte order is
            different from the architecture’s native byte order).
    This raises the question: Why not just always use the
    bt_bitfield_write functions if they optimize so well and can be
    equivalent to a memcpy()? Because barectf is designed for use in
    embedded situations where toolchains are highly varied and it is not
    a given that the C compiler used is highly optimizing, it felt safer
    to still use memcpy() when the architecture and configured trace
    byte orders match as memcpy() is guaranteed to result in a quick
    write operation.

Testing:

Add the following test cases:

 * Configuration:
    * Fail if no byte order property is defined (byte-order-no.yaml)
    * Fail if both ‘native-byte-order’ and ‘trace-byte-order’ are
       defined (byte-order-yes-two-properties.yaml)
    * Pass if only ‘trace-byte-order’ is defined (byte-order-yes.yaml)
 * Tracing:
    * Barectf 2 configuration with ‘byte-order’ big endian (assuming a
       little endian test system) (trace-byte-order-big-endian.yaml)

The configuration test files are adapted from the effective
configuration generated from the barectf test config two-packets.yaml.

For the tracing test, the configuration was adapted from the barectf 2
wiki and the test source was adapted from the barectf test source
two-packets.c.

Philippe: Document the user-facing changes in `trace-type-obj.adoc`.

Change-Id: If0e17b537795a5093c406500706ca5d1fad051c6
Signed-off-by: Erica Bugden <ebugden@efficios.com>
Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
17 files changed:
barectf/__init__.py
barectf/cgen.py
barectf/config.py
barectf/config_parse_v2.py
barectf/config_parse_v3.py
barectf/schemas/config/3/config.yaml
barectf/templates/c/bitfield.h.j2
barectf/templates/c/serialize-write-bit-array-statements.j2
barectf/templates/metadata/metadata.j2
docs/modules/yaml/pages/trace-type-obj.adoc
tests/config/yaml/3/configs/fail/type/byte-order-no.yaml [new file with mode: 0644]
tests/config/yaml/3/configs/fail/type/byte-order-yes-two-properties.yaml [new file with mode: 0644]
tests/config/yaml/3/configs/pass/type/byte-order-yes.yaml [new file with mode: 0644]
tests/tracing/configs/basic/byte-order/trace-byte-order-big-endian.yaml [new file with mode: 0644]
tests/tracing/expect/basic/byte-order/trace-byte-order-big-endian.data.expect [new file with mode: 0644]
tests/tracing/expect/basic/byte-order/trace-byte-order-big-endian.metadata.expect [new file with mode: 0644]
tests/tracing/src/basic/byte-order/trace-byte-order-big-endian.c [new file with mode: 0644]
This page took 0.027627 seconds and 4 git commands to generate.