- Common Trace Format (CTF) Specification (v1.8.1)
-
- Mathieu Desnoyers, EfficiOS Inc.
-
- The goal of the present document is to specify a trace format that suits the
- needs of the embedded, telecom, high-performance and kernel communities. It is
- based on the Common Trace Format Requirements (v1.4) document. It is designed to
- allow traces to be natively generated by the Linux kernel, Linux user-space
- applications written in C/C++, and hardware components. One major element of
- CTF is the Trace Stream Description Language (TSDL) which flexibility
- enables description of various binary trace stream layouts.
-
- The latest version of this document can be found at:
-
- git tree: git://git.efficios.com/ctf.git
- gitweb: http://git.efficios.com/?p=ctf.git
-
- A reference implementation of a library to read and write this trace format is
- being implemented within the BabelTrace project, a converter between trace
- formats. The development tree is available at:
-
- git tree: git://git.efficios.com/babeltrace.git
- gitweb: http://git.efficios.com/?p=babeltrace.git
-
- The CE Workgroup of the Linux Foundation, Ericsson, and EfficiOS have
- sponsored this work.
-
-
- Table of Contents
-
- 1. Preliminary definitions
- 2. High-level representation of a trace
- 3. Event stream
- 4. Types
- 4.1 Basic types
- 4.1.1 Type inheritance
- 4.1.2 Alignment
- 4.1.3 Byte order
- 4.1.4 Size
- 4.1.5 Integers
- 4.1.6 GNU/C bitfields
- 4.1.7 Floating point
- 4.1.8 Enumerations
- 4.2 Compound types
- 4.2.1 Structures
- 4.2.2 Variants (Discriminated/Tagged Unions)
- 4.2.3 Arrays
- 4.2.4 Sequences
- 4.2.5 Strings
- 5. Event Packet Header
- 5.1 Event Packet Header Description
- 5.2 Event Packet Context Description
- 6. Event Structure
- 6.1 Event Header
- 6.1.1 Type 1 - Few event IDs
- 6.1.2 Type 2 - Many event IDs
- 6.2 Event Context
- 6.3 Event Payload
- 6.3.1 Padding
- 6.3.2 Alignment
- 7. Trace Stream Description Language (TSDL)
- 7.1 Meta-data
- 7.2 Declaration vs Definition
- 7.3 TSDL Scopes
- 7.3.1 Lexical Scope
- 7.3.2 Static and Dynamic Scopes
- 7.4 TSDL Examples
- 8. Clocks
-
-
- 1. Preliminary definitions
-
- - Event Trace: An ordered sequence of events.
- - Event Stream: An ordered sequence of events, containing a subset of the
- trace event types.
- - Event Packet: A sequence of physically contiguous events within an event
- stream.
- - Event: This is the basic entry in a trace. (aka: a trace record).
- - An event identifier (ID) relates to the class (a type) of event within
- an event stream.
- e.g. event: irq_entry.
- - An event (or event record) relates to a specific instance of an event
- class.
- e.g. event: irq_entry, at time X, on CPU Y
- - Source Architecture: Architecture writing the trace.
- - Reader Architecture: Architecture reading the trace.
-
-
- 2. High-level representation of a trace
-
- A trace is divided into multiple event streams. Each event stream contains a
- subset of the trace event types.
-
- The final output of the trace, after its generation and optional transport over
- the network, is expected to be either on permanent or temporary storage in a
- virtual file system. Because each event stream is appended to while a trace is
- being recorded, each is associated with a distinct set of files for
- output. Therefore, a stored trace can be represented as a directory
- containing zero, one or more files per stream.
-
- Meta-data description associated with the trace contains information on
- trace event types expressed in the Trace Stream Description Language
- (TSDL). This language describes:
-
- - Trace version.
- - Types available.
- - Per-trace event header description.
- - Per-stream event header description.
- - Per-stream event context description.
- - Per-event
- - Event type to stream mapping.
- - Event type to name mapping.
- - Event type to ID mapping.
- - Event context description.
- - Event fields description.
-
-
- 3. Event stream
-
- An event stream can be divided into contiguous event packets of variable
- size. These subdivisions have a variable size. An event packet can
- contain a certain amount of padding at the end. The stream header is
- repeated at the beginning of each event packet. The rationale for the
- event stream design choices is explained in Appendix B. Stream Header
- Rationale.
-
- The event stream header will therefore be referred to as the "event packet
- header" throughout the rest of this document.
-
-
- 4. Types
-
- Types are organized as type classes. Each type class belong to either of two
- kind of types: basic types or compound types.
-
- 4.1 Basic types
-
- A basic type is a scalar type, as described in this section. It includes
- integers, GNU/C bitfields, enumerations, and floating point values.
-
- 4.1.1 Type inheritance
-
- Type specifications can be inherited to allow deriving types from a
- type class. For example, see the uint32_t named type derived from the "integer"
- type class below ("Integers" section). Types have a precise binary
- representation in the trace. A type class has methods to read and write these
- types, but must be derived into a type to be usable in an event field.
-
- 4.1.2 Alignment
-
- We define "byte-packed" types as aligned on the byte size, namely 8-bit.
- We define "bit-packed" types as following on the next bit, as defined by the
- "Integers" section.
-
- Each basic type must specify its alignment, in bits. Examples of
- possible alignments are: bit-packed (align = 1), byte-packed (align =
- 8), or word-aligned (e.g. align = 32 or align = 64). The choice depends
- on the architecture preference and compactness vs performance trade-offs
- of the implementation. Architectures providing fast unaligned write
- byte-packed basic types to save space, aligning each type on byte
- boundaries (8-bit). Architectures with slow unaligned writes align types
- on specific alignment values. If no specific alignment is declared for a
- type, it is assumed to be bit-packed for integers with size not multiple
- of 8 bits and for gcc bitfields. All other basic types are byte-packed
- by default. It is however recommended to always specify the alignment
- explicitly. Alignment values must be power of two. Compound types are
- aligned as specified in their individual specification.
-
- TSDL meta-data attribute representation of a specific alignment:
-
- align = value; /* value in bits */
-
- 4.1.3 Byte order
-
- By default, the native endianness of the source architecture the trace is used.
- Byte order can be overridden for a basic type by specifying a "byte_order"
- attribute. Typical use-case is to specify the network byte order (big endian:
- "be") to save data captured from the network into the trace without conversion.
- If not specified, the byte order is native.
-
- TSDL meta-data representation:
-
- byte_order = native OR network OR be OR le; /* network and be are aliases */
-
- 4.1.4 Size
-
- Type size, in bits, for integers and floats is that returned by "sizeof()" in C
- multiplied by CHAR_BIT.
- We require the size of "char" and "unsigned char" types (CHAR_BIT) to be fixed
- to 8 bits for cross-endianness compatibility.
-
- TSDL meta-data representation:
-
- size = value; (value is in bits)
-
- 4.1.5 Integers
-
- Signed integers are represented in two-complement. Integer alignment,
- size, signedness and byte ordering are defined in the TSDL meta-data.
- Integers aligned on byte size (8-bit) and with length multiple of byte
- size (8-bit) correspond to the C99 standard integers. In addition,
- integers with alignment and/or size that are _not_ a multiple of the
- byte size are permitted; these correspond to the C99 standard bitfields,
- with the added specification that the CTF integer bitfields have a fixed
- binary representation. A MIT-licensed reference implementation of the
- CTF portable bitfields is available at:
-
- http://git.efficios.com/?p=babeltrace.git;a=blob;f=include/babeltrace/bitfield.h
-
- Binary representation of integers:
-
- - On little and big endian:
- - Within a byte, high bits correspond to an integer high bits, and low bits
- correspond to low bits.
- - On little endian:
- - Integer across multiple bytes are placed from the less significant to the
- most significant.
- - Consecutive integers are placed from lower bits to higher bits (even within
- a byte).
- - On big endian:
- - Integer across multiple bytes are placed from the most significant to the
- less significant.
- - Consecutive integers are placed from higher bits to lower bits (even within
- a byte).
-
- This binary representation is derived from the bitfield implementation in GCC
- for little and big endian. However, contrary to what GCC does, integers can
- cross units boundaries (no padding is required). Padding can be explicitly
- added (see 4.1.6 GNU/C bitfields) to follow the GCC layout if needed.
-
- TSDL meta-data representation:
-
- integer {
- signed = true OR false; /* default false */
- byte_order = native OR network OR be OR le; /* default native */
- size = value; /* value in bits, no default */
- align = value; /* value in bits */
- /* based used for pretty-printing output, default: decimal. */
- base = decimal OR dec OR OR d OR i OR u OR 10 OR hexadecimal OR hex OR x OR X OR p OR 16
- OR octal OR oct OR o OR 8 OR binary OR b OR 2;
- /* character encoding, default: none */
- encoding = none or UTF8 or ASCII;
- }
-
- Example of type inheritance (creation of a uint32_t named type):
-
- typealias integer {
- size = 32;
- signed = false;
- align = 32;
- } := uint32_t;
-
- Definition of a named 5-bit signed bitfield:
-
- typealias integer {
- size = 5;
- signed = true;
- align = 1;
- } := int5_t;
-
- The character encoding field can be used to specify that the integer
- must be printed as a text character when read. e.g.:
-
- typealias integer {
- size = 8;
- align = 8;
- signed = false;
- encoding = UTF8;
- } := utf_char;
-
-
- 4.1.6 GNU/C bitfields
-
- The GNU/C bitfields follow closely the integer representation, with a
- particularity on alignment: if a bitfield cannot fit in the current unit, the
- unit is padded and the bitfield starts at the following unit. The unit size is
- defined by the size of the type "unit_type".
-
- TSDL meta-data representation:
-
- unit_type name:size;
-
- As an example, the following structure declared in C compiled by GCC:
-
- struct example {
- short a:12;
- short b:5;
- };
-
- The example structure is aligned on the largest element (short). The second
- bitfield would be aligned on the next unit boundary, because it would not fit in
- the current unit.
-
- 4.1.7 Floating point
-
- The floating point values byte ordering is defined in the TSDL meta-data.
-
- Floating point values follow the IEEE 754-2008 standard interchange formats.
- Description of the floating point values include the exponent and mantissa size
- in bits. Some requirements are imposed on the floating point values:
-
- - FLT_RADIX must be 2.
- - mant_dig is the number of digits represented in the mantissa. It is specified
- by the ISO C99 standard, section 5.2.4, as FLT_MANT_DIG, DBL_MANT_DIG and
- LDBL_MANT_DIG as defined by <float.h>.
- - exp_dig is the number of digits represented in the exponent. Given that
- mant_dig is one bit more than its actual size in bits (leading 1 is not
- needed) and also given that the sign bit always takes one bit, exp_dig can be
- specified as:
-
- - sizeof(float) * CHAR_BIT - FLT_MANT_DIG
- - sizeof(double) * CHAR_BIT - DBL_MANT_DIG
- - sizeof(long double) * CHAR_BIT - LDBL_MANT_DIG
-
- TSDL meta-data representation:
-
- floating_point {
- exp_dig = value;
- mant_dig = value;
- byte_order = native OR network OR be OR le;
- align = value;
- }
-
- Example of type inheritance:
-
- typealias floating_point {
- exp_dig = 8; /* sizeof(float) * CHAR_BIT - FLT_MANT_DIG */
- mant_dig = 24; /* FLT_MANT_DIG */
- byte_order = native;
- align = 32;
- } := float;
-
- TODO: define NaN, +inf, -inf behavior.
-
- Bit-packed, byte-packed or larger alignments can be used for floating
- point values, similarly to integers.
-
- 4.1.8 Enumerations
-
- Enumerations are a mapping between an integer type and a table of strings. The
- numerical representation of the enumeration follows the integer type specified
- by the meta-data. The enumeration mapping table is detailed in the enumeration
- description within the meta-data. The mapping table maps inclusive value
- ranges (or single values) to strings. Instead of being limited to simple
- "value -> string" mappings, these enumerations map
- "[ start_value ... end_value ] -> string", which map inclusive ranges of
- values to strings. An enumeration from the C language can be represented in
- this format by having the same start_value and end_value for each element, which
- is in fact a range of size 1. This single-value range is supported without
- repeating the start and end values with the value = string declaration.
-
- enum name : integer_type {
- somestring = start_value1 ... end_value1,
- "other string" = start_value2 ... end_value2,
- yet_another_string, /* will be assigned to end_value2 + 1 */
- "some other string" = value,
- ...
- };
-
- If the values are omitted, the enumeration starts at 0 and increment of 1 for
- each entry:
-
- enum name : unsigned int {
- ZERO,
- ONE,
- TWO,
- TEN = 10,
- ELEVEN,
- };
-
- Overlapping ranges within a single enumeration are implementation defined.
-
- A nameless enumeration can be declared as a field type or as part of a typedef:
-
- enum : integer_type {
- ...
- }
-
- Enumerations omitting the container type ": integer_type" use the "int"
- type (for compatibility with C99). The "int" type must be previously
- declared. E.g.:
-
- typealias integer { size = 32; align = 32; signed = true } := int;
-
- enum {
- ...
- }
-
-
- 4.2 Compound types
-
- Compound are aggregation of type declarations. Compound types include
- structures, variant, arrays, sequences, and strings.
-
- 4.2.1 Structures
-
- Structures are aligned on the largest alignment required by basic types
- contained within the structure. (This follows the ISO/C standard for structures)
-
- TSDL meta-data representation of a named structure:
-
- struct name {
- field_type field_name;
- field_type field_name;
- ...
- };
-
- Example:
-
- struct example {
- integer { /* Nameless type */
- size = 16;
- signed = true;
- align = 16;
- } first_field_name;
- uint64_t second_field_name; /* Named type declared in the meta-data */
- };
-
- The fields are placed in a sequence next to each other. They each
- possess a field name, which is a unique identifier within the structure.
- The identifier is not allowed to use any reserved keyword
- (see Section C.1.2). Replacing reserved keywords with
- underscore-prefixed field names is recommended. Fields starting with an
- underscore should have their leading underscore removed by the CTF trace
- readers.
-
- A nameless structure can be declared as a field type or as part of a typedef:
-
- struct {
- ...
- }
-
- Alignment for a structure compound type can be forced to a minimum value
- by adding an "align" specifier after the declaration of a structure
- body. This attribute is read as: align(value). The value is specified in
- bits. The structure will be aligned on the maximum value between this
- attribute and the alignment required by the basic types contained within
- the structure. e.g.
-
- struct {
- ...
- } align(32)
-
- 4.2.2 Variants (Discriminated/Tagged Unions)
-
- A CTF variant is a selection between different types. A CTF variant must
- always be defined within the scope of a structure or within fields
- contained within a structure (defined recursively). A "tag" enumeration
- field must appear in either the same static scope, prior to the variant
- field (in field declaration order), in an upper static scope , or in an
- upper dynamic scope (see Section 7.3.2). The type selection is indicated
- by the mapping from the enumeration value to the string used as variant
- type selector. The field to use as tag is specified by the "tag_field",
- specified between "< >" after the "variant" keyword for unnamed
- variants, and after "variant name" for named variants.
-
- The alignment of the variant is the alignment of the type as selected by the tag
- value for the specific instance of the variant. The alignment of the type
- containing the variant is independent of the variant alignment. The size of the
- variant is the size as selected by the tag value for the specific instance of
- the variant.
-
- Each variant type selector possess a field name, which is a unique
- identifier within the variant. The identifier is not allowed to use any
- reserved keyword (see Section C.1.2). Replacing reserved keywords with
- underscore-prefixed field names is recommended. Fields starting with an
- underscore should have their leading underscore removed by the CTF trace
- readers.
-
-
- A named variant declaration followed by its definition within a structure
- declaration:
-
- variant name {
- field_type sel1;
- field_type sel2;
- field_type sel3;
- ...
- };
-
- struct {
- enum : integer_type { sel1, sel2, sel3, ... } tag_field;
- ...
- variant name <tag_field> v;
- }
-
- An unnamed variant definition within a structure is expressed by the following
- TSDL meta-data:
-
- struct {
- enum : integer_type { sel1, sel2, sel3, ... } tag_field;
- ...
- variant <tag_field> {
- field_type sel1;
- field_type sel2;
- field_type sel3;
- ...
- } v;
- }
-
- Example of a named variant within a sequence that refers to a single tag field:
-
- variant example {
- uint32_t a;
- uint64_t b;
- short c;
- };
-
- struct {
- enum : uint2_t { a, b, c } choice;
- unsigned int seqlen;
- variant example <choice> v[seqlen];
- }
-
- Example of an unnamed variant:
-
- struct {
- enum : uint2_t { a, b, c, d } choice;
- /* Unrelated fields can be added between the variant and its tag */
- int32_t somevalue;
- variant <choice> {
- uint32_t a;
- uint64_t b;
- short c;
- struct {
- unsigned int field1;
- uint64_t field2;
- } d;
- } s;
- }
-
- Example of an unnamed variant within an array:
-
- struct {
- enum : uint2_t { a, b, c } choice;
- variant <choice> {
- uint32_t a;
- uint64_t b;
- short c;
- } v[10];
- }
-
- Example of a variant type definition within a structure, where the defined type
- is then declared within an array of structures. This variant refers to a tag
- located in an upper static scope. This example clearly shows that a variant
- type definition referring to the tag "x" uses the closest preceding field from
- the static scope of the type definition.
-
- struct {
- enum : uint2_t { a, b, c, d } x;
-
- typedef variant <x> { /*
- * "x" refers to the preceding "x" enumeration in the
- * static scope of the type definition.
- */
- uint32_t a;
- uint64_t b;
- short c;
- } example_variant;
-
- struct {
- enum : int { x, y, z } x; /* This enumeration is not used by "v". */
- example_variant v; /*
- * "v" uses the "enum : uint2_t { a, b, c, d }"
- * tag.
- */
- } a[10];
- }
-
- 4.2.3 Arrays
-
- Arrays are fixed-length. Their length is declared in the type
- declaration within the meta-data. They contain an array of "inner type"
- elements, which can refer to any type not containing the type of the
- array being declared (no circular dependency). The length is the number
- of elements in an array.
-
- TSDL meta-data representation of a named array:
-
- typedef elem_type name[length];
-
- A nameless array can be declared as a field type within a structure, e.g.:
-
- uint8_t field_name[10];
-
- Arrays are always aligned on their element alignment requirement.
-
- 4.2.4 Sequences
-
- Sequences are dynamically-sized arrays. They refer to a a "length"
- unsigned integer field, which must appear in either the same static scope,
- prior to the sequence field (in field declaration order), in an upper
- static scope, or in an upper dynamic scope (see Section 7.3.2). This
- length field represents the number of elements in the sequence. The
- sequence per se is an array of "inner type" elements.
-
- TSDL meta-data representation for a sequence type definition:
-
- struct {
- unsigned int length_field;
- typedef elem_type typename[length_field];
- typename seq_field_name;
- }
-
- A sequence can also be declared as a field type, e.g.:
-
- struct {
- unsigned int length_field;
- long seq_field_name[length_field];
- }
-
- Multiple sequences can refer to the same length field, and these length
- fields can be in a different upper dynamic scope:
-
- e.g., assuming the stream.event.header defines:
-
- stream {
- ...
- id = 1;
- event.header := struct {
- uint16_t seq_len;
- };
- };
-
- event {
- ...
- stream_id = 1;
- fields := struct {
- long seq_a[stream.event.header.seq_len];
- char seq_b[stream.event.header.seq_len];
- };
- };
-
- The sequence elements follow the "array" specifications.
-
- 4.2.5 Strings
-
- Strings are an array of bytes of variable size and are terminated by a '\0'
- "NULL" character. Their encoding is described in the TSDL meta-data. In
- absence of encoding attribute information, the default encoding is
- UTF-8.
-
- TSDL meta-data representation of a named string type:
-
- typealias string {
- encoding = UTF8 OR ASCII;
- } := name;
-
- A nameless string type can be declared as a field type:
-
- string field_name; /* Use default UTF8 encoding */
-
- Strings are always aligned on byte size.
-
- 5. Event Packet Header
-
- The event packet header consists of two parts: the "event packet header"
- is the same for all streams of a trace. The second part, the "event
- packet context", is described on a per-stream basis. Both are described
- in the TSDL meta-data. The packets are aligned on architecture-page-sized
- addresses.
-
- Event packet header (all fields are optional, specified by TSDL meta-data):
-
- - Magic number (CTF magic number: 0xC1FC1FC1) specifies that this is a
- CTF packet. This magic number is optional, but when present, it should
- come at the very beginning of the packet.
- - Trace UUID, used to ensure the event packet match the meta-data used.
- (note: we cannot use a meta-data checksum in every cases instead of a
- UUID because meta-data can be appended to while tracing is active)
- This field is optional.
- - Stream ID, used as reference to stream description in meta-data.
- This field is optional if there is only one stream description in the
- meta-data, but becomes required if there are more than one stream in
- the TSDL meta-data description.
-
- Event packet context (all fields are optional, specified by TSDL meta-data):
-
- - Event packet content size (in bits).
- - Event packet size (in bits, includes padding).
- - Event packet content checksum. Checksum excludes the event packet
- header.
- - Per-stream event packet sequence count (to deal with UDP packet loss). The
- number of significant sequence counter bits should also be present, so
- wrap-arounds are dealt with correctly.
- - Time-stamp at the beginning and time-stamp at the end of the event packet.
- Both timestamps are written in the packet header, but sampled respectively
- while (or before) writing the first event and while (or after) writing the
- last event in the packet. The inclusive range between these timestamps should
- include all event timestamps assigned to events contained within the packet.
- - Events discarded count
- - Snapshot of a per-stream free-running counter, counting the number of
- events discarded that were supposed to be written in the stream prior to
- the first event in the event packet.
- * Note: producer-consumer buffer full condition should fill the current
- event packet with padding so we know exactly where events have been
- discarded.
- - Lossless compression scheme used for the event packet content. Applied
- directly to raw data. New types of compression can be added in following
- versions of the format.
- 0: no compression scheme
- 1: bzip2
- 2: gzip
- 3: xz
- - Cypher used for the event packet content. Applied after compression.
- 0: no encryption
- 1: AES
- - Checksum scheme used for the event packet content. Applied after encryption.
- 0: no checksum
- 1: md5
- 2: sha1
- 3: crc32
-
- 5.1 Event Packet Header Description
-
- The event packet header layout is indicated by the trace packet.header
- field. Here is a recommended structure type for the packet header with
- the fields typically expected (although these fields are each optional):
-
- struct event_packet_header {
- uint32_t magic;
- uint8_t uuid[16];
- uint32_t stream_id;
- };
-
- trace {
- ...
- packet.header := struct event_packet_header;
- };
-
- If the magic number is not present, tools such as "file" will have no
- mean to discover the file type.
-
- If the uuid is not present, no validation that the meta-data actually
- corresponds to the stream is performed.
-
- If the stream_id packet header field is missing, the trace can only
- contain a single stream. Its "id" field can be left out, and its events
- don't need to declare a "stream_id" field.
- 5.2 Event Packet Context Description
- Event packet context example. These are declared within the stream declaration
- in the meta-data. All these fields are optional. If the packet size field is
- missing, the whole stream only contains a single packet. If the content
- size field is missing, the packet is filled (no padding). The content
- and packet sizes include all headers.
- An example event packet context type:
- struct event_packet_context {
- uint64_t timestamp_begin;
- uint64_t timestamp_end;
- uint32_t checksum;
- uint32_t stream_packet_count;
- uint32_t events_discarded;
- uint32_t cpu_id;
- uint32_t/uint16_t content_size;
- uint32_t/uint16_t packet_size;
- uint8_t compression_scheme;
- uint8_t encryption_scheme;
- uint8_t checksum_scheme;
- };
- 6. Event Structure
- The overall structure of an event is:
- 1 - Stream Packet Context (as specified by the stream meta-data)
- 2 - Event Header (as specified by the stream meta-data)
- 3 - Stream Event Context (as specified by the stream meta-data)
- 4 - Event Context (as specified by the event meta-data)
- 5 - Event Payload (as specified by the event meta-data)
- This structure defines an implicit dynamic scoping, where variants
- located in inner structures (those with a higher number in the listing
- above) can refer to the fields of outer structures (with lower number in
- the listing above). See Section 7.3 TSDL Scopes for more detail.
- 6.1 Event Header
- Event headers can be described within the meta-data. We hereby propose, as an
- example, two types of events headers. Type 1 accommodates streams with less than
- 31 event IDs. Type 2 accommodates streams with 31 or more event IDs.
- One major factor can vary between streams: the number of event IDs assigned to
- a stream. Luckily, this information tends to stay relatively constant (modulo
- event registration while trace is being recorded), so we can specify different
- representations for streams containing few event IDs and streams containing
- many event IDs, so we end up representing the event ID and time-stamp as
- densely as possible in each case.
- The header is extended in the rare occasions where the information cannot be
- represented in the ranges available in the standard event header. They are also
- used in the rare occasions where the data required for a field could not be
- collected: the flag corresponding to the missing field within the missing_fields
- array is then set to 1.
- Types uintX_t represent an X-bit unsigned integer, as declared with
- either:
- typealias integer { size = X; align = X; signed = false } := uintX_t;
- or
- typealias integer { size = X; align = 1; signed = false } := uintX_t;
- 6.1.1 Type 1 - Few event IDs
- - Aligned on 32-bit (or 8-bit if byte-packed, depending on the architecture
- preference).
- - Native architecture byte ordering.
- - For "compact" selection
- - Fixed size: 32 bits.
- - For "extended" selection
- - Size depends on the architecture and variant alignment.
- struct event_header_1 {
- /*
- * id: range: 0 - 30.
- * id 31 is reserved to indicate an extended header.
- */
- enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
- variant <id> {
- struct {
- uint27_t timestamp;
- } compact;
- struct {
- uint32_t id; /* 32-bit event IDs */
- uint64_t timestamp; /* 64-bit timestamps */
- } extended;
- } v;
- } align(32); /* or align(8) */
- 6.1.2 Type 2 - Many event IDs
- - Aligned on 16-bit (or 8-bit if byte-packed, depending on the architecture
- preference).
- - Native architecture byte ordering.
- - For "compact" selection
- - Size depends on the architecture and variant alignment.
- - For "extended" selection
- - Size depends on the architecture and variant alignment.
- struct event_header_2 {
- /*
- * id: range: 0 - 65534.
- * id 65535 is reserved to indicate an extended header.
- */
- enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
- variant <id> {
- struct {
- uint32_t timestamp;
- } compact;
- struct {
- uint32_t id; /* 32-bit event IDs */
- uint64_t timestamp; /* 64-bit timestamps */
- } extended;
- } v;
- } align(16); /* or align(8) */
- 6.2 Event Context
- The event context contains information relative to the current event.
- The choice and meaning of this information is specified by the TSDL
- stream and event meta-data descriptions. The stream context is applied
- to all events within the stream. The stream context structure follows
- the event header. The event context is applied to specific events. Its
- structure follows the stream context structure.
- An example of stream-level event context is to save the event payload size with
- each event, or to save the current PID with each event. These are declared
- within the stream declaration within the meta-data:
- stream {
- ...
- event.context := struct {
- uint pid;
- uint16_t payload_size;
- };
- };
- An example of event-specific event context is to declare a bitmap of missing
- fields, only appended after the stream event context if the extended event
- header is selected. NR_FIELDS is the number of fields within the event (a
- numeric value).
- event {
- context = struct {
- variant <id> {
- struct { } compact;
- struct {
- uint1_t missing_fields[NR_FIELDS]; /* missing event fields bitmap */
- } extended;
- } v;
- };
- ...
- }
- 6.3 Event Payload
- An event payload contains fields specific to a given event type. The fields
- belonging to an event type are described in the event-specific meta-data
- within a structure type.
- 6.3.1 Padding
- No padding at the end of the event payload. This differs from the ISO/C standard
- for structures, but follows the CTF standard for structures. In a trace, even
- though it makes sense to align the beginning of a structure, it really makes no
- sense to add padding at the end of the structure, because structures are usually
- not followed by a structure of the same type.
- This trick can be done by adding a zero-length "end" field at the end of the C
- structures, and by using the offset of this field rather than using sizeof()
- when calculating the size of a structure (see Appendix "A. Helper macros").
- 6.3.2 Alignment
- The event payload is aligned on the largest alignment required by types
- contained within the payload. (This follows the ISO/C standard for structures)
- 7. Trace Stream Description Language (TSDL)
- The Trace Stream Description Language (TSDL) allows expression of the
- binary trace streams layout in a C99-like Domain Specific Language
- (DSL).
- 7.1 Meta-data
- The trace stream layout description is located in the trace meta-data.
- The meta-data is itself located in a stream identified by its name:
- "metadata".
- The meta-data description can be expressed in two different formats:
- text-only and packet-based. The text-only description facilitates
- generation of meta-data and provides a convenient way to enter the
- meta-data information by hand. The packet-based meta-data provides the
- CTF stream packet facilities (checksumming, compression, encryption,
- network-readiness) for meta-data stream generated and transported by a
- tracer.
- The text-only meta-data file is a plain-text TSDL description. This file
- must begin with the following characters to identify the file as a CTF
- TSDL text-based metadata file (without the double-quotes) :
- "/* CTF"
- It must be followed by a space, and the version of the specification
- followed by the CTF trace, e.g.:
- " 1.8"
- These characters allow automated discovery of file type and CTF
- specification version. They are interpreted as a the beginning of a
- comment by the TSDL metadata parser. The comment can be continued to
- contain extra commented characters before it is closed.
- The packet-based meta-data is made of "meta-data packets", which each
- start with a meta-data packet header. The packet-based meta-data
- description is detected by reading the magic number "0x75D11D57" at the
- beginning of the file. This magic number is also used to detect the
- endianness of the architecture by trying to read the CTF magic number
- and its counterpart in reversed endianness. The events within the
- meta-data stream have no event header nor event context. Each event only
- contains a "sequence" payload, which is a sequence of bits using the
- "trace.packet.header.content_size" field as a placeholder for its length
- (the packet header size should be substracted). The formatting of this
- sequence of bits is a plain-text representation of the TSDL description.
- Each meta-data packet start with a special packet header, specific to
- the meta-data stream, which contains, exactly:
- struct metadata_packet_header {
- uint32_t magic; /* 0x75D11D57 */
- uint8_t uuid[16]; /* Unique Universal Identifier */
- uint32_t checksum; /* 0 if unused */
- uint32_t content_size; /* in bits */
- uint32_t packet_size; /* in bits */
- uint8_t compression_scheme; /* 0 if unused */
- uint8_t encryption_scheme; /* 0 if unused */
- uint8_t checksum_scheme; /* 0 if unused */
- uint8_t major; /* CTF spec version major number */
- uint8_t minor; /* CTF spec version minor number */
- };
- The packet-based meta-data can be converted to a text-only meta-data by
- concatenating all the strings in contains.
- In the textual representation of the meta-data, the text contained
- within "/*" and "*/", as well as within "//" and end of line, are
- treated as comments. Boolean values can be represented as true, TRUE,
- or 1 for true, and false, FALSE, or 0 for false. Within the string-based
- meta-data description, the trace UUID is represented as a string of
- hexadecimal digits and dashes "-". In the event packet header, the trace
- UUID is represented as an array of bytes.
- 7.2 Declaration vs Definition
- A declaration associates a layout to a type, without specifying where
- this type is located in the event structure hierarchy (see Section 6).
- This therefore includes typedef, typealias, as well as all type
- specifiers. In certain circumstances (typedef, structure field and
- variant field), a declaration is followed by a declarator, which specify
- the newly defined type name (for typedef), or the field name (for
- declarations located within structure and variants). Array and sequence,
- declared with square brackets ("[" "]"), are part of the declarator,
- similarly to C99. The enumeration base type is specified by
- ": enum_base", which is part of the type specifier. The variant tag
- name, specified between "<" ">", is also part of the type specifier.
- A definition associates a type to a location in the event structure
- hierarchy (see Section 6). This association is denoted by ":=", as shown
- in Section 7.3.
- 7.3 TSDL Scopes
- TSDL uses three different types of scoping: a lexical scope is used for
- declarations and type definitions, and static and dynamic scopes are
- used for variants references to tag fields (with relative and absolute
- path lookups) and for sequence references to length fields.
- 7.3.1 Lexical Scope
- Each of "trace", "env", "stream", "event", "struct" and "variant" have
- their own nestable declaration scope, within which types can be declared
- using "typedef" and "typealias". A root declaration scope also contains
- all declarations located outside of any of the aforementioned
- declarations. An inner declaration scope can refer to type declared
- within its container lexical scope prior to the inner declaration scope.
- Redefinition of a typedef or typealias is not valid, although hiding an
- upper scope typedef or typealias is allowed within a sub-scope.
- 7.3.2 Static and Dynamic Scopes
- A local static scope consists in the scope generated by the declaration
- of fields within a compound type. A static scope is a local static scope
- augmented with the nested sub-static-scopes it contains.
- A dynamic scope consists in the static scope augmented with the
- implicit event structure definition hierarchy presented at Section 6.
- Multiple declarations of the same field name within a local static scope
- is not valid. It is however valid to re-use the same field name in
- different local scopes.
- Nested static and dynamic scopes form lookup paths. These are used for
- variant tag and sequence length references. They are used at the variant
- and sequence definition site to look up the location of the tag field
- associated with a variant, and to lookup up the location of the length
- field associated with a sequence.
- Variants and sequences can refer to a tag field either using a relative
- path or an absolute path. The relative path is relative to the scope in
- which the variant or sequence performing the lookup is located.
- Relative paths are only allowed to lookup within the same static scope,
- which includes its nested static scopes. Lookups targeting parent static
- scopes need to be performed with an absolute path.
- Absolute path lookups use the full path including the dynamic scope
- followed by a "." and then the static scope. Therefore, variants (or
- sequences) in lower levels in the dynamic scope (e.g. event context) can
- refer to a tag (or length) field located in upper levels (e.g. in the
- event header) by specifying, in this case, the associated tag with
- <stream.event.header.field_name>. This allows, for instance, the event
- context to define a variant referring to the "id" field of the event
- header as selector.
- The dynamic scope prefixes are thus:
- - Trace Environment: <env. >,
- - Trace Packet Header: <trace.packet.header. >,
- - Stream Packet Context: <stream.packet.context. >,
- - Event Header: <stream.event.header. >,
- - Stream Event Context: <stream.event.context. >,
- - Event Context: <event.context. >,
- - Event Payload: <event.fields. >.
- The target dynamic scope must be specified explicitly when referring to
- a field outside of the static scope (absolute scope reference). No
- conflict can occur between relative and dynamic paths, because the
- keywords "trace", "stream", and "event" are reserved, and thus
- not permitted as field names. It is recommended that field names
- clashing with CTF and C99 reserved keywords use an underscore prefix to
- eliminate the risk of generating a description containing an invalid
- field name. Consequently, fields starting with an underscore should have
- their leading underscore removed by the CTF trace readers.
- The information available in the dynamic scopes can be thought of as the
- current tracing context. At trace production, information about the
- current context is saved into the specified scope field levels. At trace
- consumption, for each event, the current trace context is therefore
- readable by accessing the upper dynamic scopes.
- 7.4 TSDL Examples
- The grammar representing the TSDL meta-data is presented in Appendix C.
- TSDL Grammar. This section presents a rather lighter reading that
- consists in examples of TSDL meta-data, with template values.
- The stream "id" can be left out if there is only one stream in the
- trace. The event "id" field can be left out if there is only one event
- in a stream.
- trace {
- major = value; /* CTF spec version major number */
- minor = value; /* CTF spec version minor number */
- uuid = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"; /* Trace UUID */
- byte_order = be OR le; /* Endianness (required) */
- packet.header := struct {
- uint32_t magic;
- uint8_t uuid[16];
- uint32_t stream_id;
- };
- };
- /*
- * The "env" (environment) scope contains assignment expressions. The
- * field names and content are implementation-defined.
- */
- env {
- pid = value; /* example */
- proc_name = "name"; /* example */
- ...
- };
- stream {
- id = stream_id;
- /* Type 1 - Few event IDs; Type 2 - Many event IDs. See section 6.1. */
- event.header := event_header_1 OR event_header_2;
- event.context := struct {
- ...
- };
- packet.context := struct {
- ...
- };
- };
- event {
- name = "event_name";
- id = value; /* Numeric identifier within the stream */
- stream_id = stream_id;
- loglevel = value;
- context := struct {
- ...
- };
- fields := struct {
- ...
- };
- };
- /* More detail on types in section 4. Types */
- /*
- * Named types:
- *
- * Type declarations behave similarly to the C standard.
- */
- typedef aliased_type_specifiers new_type_declarators;
- /* e.g.: typedef struct example new_type_name[10]; */
- /*
- * typealias
- *
- * The "typealias" declaration can be used to give a name (including
- * pointer declarator specifier) to a type. It should also be used to
- * map basic C types (float, int, unsigned long, ...) to a CTF type.
- * Typealias is a superset of "typedef": it also allows assignment of a
- * simple variable identifier to a type.
- */
- typealias type_class {
- ...
- } := type_specifiers type_declarator;
- /*
- * e.g.:
- * typealias integer {
- * size = 32;
- * align = 32;
- * signed = false;
- * } := struct page *;
- *
- * typealias integer {
- * size = 32;
- * align = 32;
- * signed = true;
- * } := int;
- */
- struct name {
- ...
- };
- variant name {
- ...
- };
- enum name : integer_type {
- ...
- };
- /*
- * Unnamed types, contained within compound type fields, typedef or typealias.
- */
- struct {
- ...
- }
- struct {
- ...
- } align(value)
- variant {
- ...
- }
- enum : integer_type {
- ...
- }
- typedef type new_type[length];
- struct {
- type field_name[length];
- }
- typedef type new_type[length_type];
- struct {
- type field_name[length_type];
- }
- integer {
- ...
- }
- floating_point {
- ...
- }
- struct {
- integer_type field_name:size; /* GNU/C bitfield */
- }
- struct {
- string field_name;
- }
- 8. Clocks
- Clock metadata allows to describe the clock topology of the system, as
- well as to detail each clock parameter. In absence of clock description,
- it is assumed that all fields named "timestamp" use the same clock
- source, which increments once per nanosecond.
- Describing a clock and how it is used by streams is threefold: first,
- the clock and clock topology should be described in a "clock"
- description block, e.g.:
- clock {
- name = cycle_counter_sync;
- uuid = "62189bee-96dc-11e0-91a8-cfa3d89f3923";
- description = "Cycle counter synchronized across CPUs";
- freq = 1000000000; /* frequency, in Hz */
- /* precision in seconds is: 1000 * (1/freq) */
- precision = 1000;
- /*
- * clock value offset from Epoch is:
- * offset_s + (offset * (1/freq))
- */
- offset_s = 1326476837;
- offset = 897235420;
- absolute = FALSE;
- };
- The mandatory "name" field specifies the name of the clock identifier,
- which can later be used as a reference. The optional field "uuid" is the
- unique identifier of the clock. It can be used to correlate different
- traces that use the same clock. An optional textual description string
- can be added with the "description" field. The "freq" field is the
- initial frequency of the clock, in Hz. If the "freq" field is not
- present, the frequency is assumed to be 1000000000 (providing clock
- increment of 1 ns). The optional "precision" field details the
- uncertainty on the clock measurements, in (1/freq) units. The "offset_s"
- and "offset" fields indicate the offset from POSIX.1 Epoch, 1970-01-01
- 00:00:00 +0000 (UTC), to the zero of value of the clock. The "offset_s"
- field is in seconds. The "offset" field is in (1/freq) units. If any of
- the "offset_s" or "offset" field is not present, it is assigned the 0
- value. The field "absolute" is TRUE if the clock is a global reference
- across different clock uuid (e.g. NTP time). Otherwise, "absolute" is
- FALSE, and the clock can be considered as synchronized only with other
- clocks that have the same uuid.
- Secondly, a reference to this clock should be added within an integer
- type:
- typealias integer {
- size = 64; align = 1; signed = false;
- map = clock.cycle_counter_sync.value;
- } := uint64_ccnt_t;
- Thirdly, stream declarations can reference the clock they use as a
- time-stamp source:
- struct packet_context {
- uint64_ccnt_t ccnt_begin;
- uint64_ccnt_t ccnt_end;
- /* ... */
- };
- stream {
- /* ... */
- event.header := struct {
- uint64_ccnt_t timestamp;
- /* ... */
- }
- packet.context := struct packet_context;
- };
- For a N-bit integer type referring to a clock, if the integer overflows
- compared to the N low order bits of the clock prior value, then it is
- assumed that one, and only one, overflow occurred. It is therefore
- important that events encoding time on a small number of bits happen
- frequently enough to detect when more than one N-bit overflow occurs.
- In a packet context, clock field names ending with "_begin" and "_end"
- have a special meaning: this refers to the time-stamps at, respectively,
- the beginning and the end of each packet.
- A. Helper macros
- The two following macros keep track of the size of a GNU/C structure without
- padding at the end by placing HEADER_END as the last field. A one byte end field
- is used for C90 compatibility (C99 flexible arrays could be used here). Note
- that this does not affect the effective structure size, which should always be
- calculated with the header_sizeof() helper.
- #define HEADER_END char end_field
- #define header_sizeof(type) offsetof(typeof(type), end_field)
- B. Stream Header Rationale
- An event stream is divided in contiguous event packets of variable size. These
- subdivisions allow the trace analyzer to perform a fast binary search by time
- within the stream (typically requiring to index only the event packet headers)
- without reading the whole stream. These subdivisions have a variable size to
- eliminate the need to transfer the event packet padding when partially filled
- event packets must be sent when streaming a trace for live viewing/analysis.
- An event packet can contain a certain amount of padding at the end. Dividing
- streams into event packets is also useful for network streaming over UDP and
- flight recorder mode tracing (a whole event packet can be swapped out of the
- buffer atomically for reading).
- The stream header is repeated at the beginning of each event packet to allow
- flexibility in terms of:
- - streaming support,
- - allowing arbitrary buffers to be discarded without making the trace
- unreadable,
- - allow UDP packet loss handling by either dealing with missing event packet
- or asking for re-transmission.
- - transparently support flight recorder mode,
- - transparently support crash dump.
- C. TSDL Grammar
- /*
- * Common Trace Format (CTF) Trace Stream Description Language (TSDL) Grammar.
- *
- * Inspired from the C99 grammar:
- * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf (Annex A)
- * and c++1x grammar (draft)
- * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3291.pdf (Annex A)
- *
- * Specialized for CTF needs by including only constant and declarations from
- * C99 (excluding function declarations), and by adding support for variants,
- * sequences and CTF-specific specifiers. Enumeration container types
- * semantic is inspired from c++1x enum-base.
- */
- 1) Lexical grammar
- 1.1) Lexical elements
- token:
- keyword
- identifier
- constant
- string-literal
- punctuator
- 1.2) Keywords
- keyword: is one of
- align
- const
- char
- clock
- double
- enum
- env
- event
- floating_point
- float
- integer
- int
- long
- short
- signed
- stream
- string
- struct
- trace
- typealias
- typedef
- unsigned
- variant
- void
- _Bool
- _Complex
- _Imaginary
- 1.3) Identifiers
- identifier:
- identifier-nondigit
- identifier identifier-nondigit
- identifier digit
- identifier-nondigit:
- nondigit
- universal-character-name
- any other implementation-defined characters
- nondigit:
- _
- [a-zA-Z] /* regular expression */
- digit:
- [0-9] /* regular expression */
- 1.4) Universal character names
- universal-character-name:
- \u hex-quad
- \U hex-quad hex-quad
- hex-quad:
- hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit
- 1.5) Constants
- constant:
- integer-constant
- enumeration-constant
- character-constant
- integer-constant:
- decimal-constant integer-suffix-opt
- octal-constant integer-suffix-opt
- hexadecimal-constant integer-suffix-opt
- decimal-constant:
- nonzero-digit
- decimal-constant digit
- octal-constant:
- 0
- octal-constant octal-digit
- hexadecimal-constant:
- hexadecimal-prefix hexadecimal-digit
- hexadecimal-constant hexadecimal-digit
- hexadecimal-prefix:
- 0x
- 0X
- nonzero-digit:
- [1-9]
- integer-suffix:
- unsigned-suffix long-suffix-opt
- unsigned-suffix long-long-suffix
- long-suffix unsigned-suffix-opt
- long-long-suffix unsigned-suffix-opt
- unsigned-suffix:
- u
- U
- long-suffix:
- l
- L
- long-long-suffix:
- ll
- LL
- enumeration-constant:
- identifier
- string-literal
- character-constant:
- ' c-char-sequence '
- L' c-char-sequence '
- c-char-sequence:
- c-char
- c-char-sequence c-char
- c-char:
- any member of source charset except single-quote ('), backslash
- (\), or new-line character.
- escape-sequence
-
- escape-sequence:
- simple-escape-sequence
- octal-escape-sequence
- hexadecimal-escape-sequence
- universal-character-name
-
- simple-escape-sequence: one of
- \' \" \? \\ \a \b \f \n \r \t \v
- octal-escape-sequence:
- \ octal-digit
- \ octal-digit octal-digit
- \ octal-digit octal-digit octal-digit
- hexadecimal-escape-sequence:
- \x hexadecimal-digit
- hexadecimal-escape-sequence hexadecimal-digit
- 1.6) String literals
- string-literal:
- " s-char-sequence-opt "
- L" s-char-sequence-opt "
- s-char-sequence:
- s-char
- s-char-sequence s-char
- s-char:
- any member of source charset except double-quote ("), backslash
- (\), or new-line character.
- escape-sequence
- 1.7) Punctuators
- punctuator: one of
- [ ] ( ) { } . -> * + - < > : ; ... = ,
- 2) Phrase structure grammar
- primary-expression:
- identifier
- constant
- string-literal
- ( unary-expression )
- postfix-expression:
- primary-expression
- postfix-expression [ unary-expression ]
- postfix-expression . identifier
- postfix-expressoin -> identifier
- unary-expression:
- postfix-expression
- unary-operator postfix-expression
- unary-operator: one of
- + -
- assignment-operator:
- =
- type-assignment-operator:
- :=
- constant-expression-range:
- unary-expression ... unary-expression
- 2.2) Declarations:
- declaration:
- declaration-specifiers declarator-list-opt ;
- ctf-specifier ;
- declaration-specifiers:
- storage-class-specifier declaration-specifiers-opt
- type-specifier declaration-specifiers-opt
- type-qualifier declaration-specifiers-opt
- declarator-list:
- declarator
- declarator-list , declarator
- abstract-declarator-list:
- abstract-declarator
- abstract-declarator-list , abstract-declarator
- storage-class-specifier:
- typedef
- type-specifier:
- void
- char
- short
- int
- long
- float
- double
- signed
- unsigned
- _Bool
- _Complex
- _Imaginary
- struct-specifier
- variant-specifier
- enum-specifier
- typedef-name
- ctf-type-specifier
- align-attribute:
- align ( unary-expression )
- struct-specifier:
- struct identifier-opt { struct-or-variant-declaration-list-opt } align-attribute-opt
- struct identifier align-attribute-opt
- struct-or-variant-declaration-list:
- struct-or-variant-declaration
- struct-or-variant-declaration-list struct-or-variant-declaration
- struct-or-variant-declaration:
- specifier-qualifier-list struct-or-variant-declarator-list ;
- declaration-specifiers-opt storage-class-specifier declaration-specifiers-opt declarator-list ;
- typealias declaration-specifiers abstract-declarator-list type-assignment-operator declaration-specifiers abstract-declarator-list ;
- typealias declaration-specifiers abstract-declarator-list type-assignment-operator declarator-list ;
- specifier-qualifier-list:
- type-specifier specifier-qualifier-list-opt
- type-qualifier specifier-qualifier-list-opt
- struct-or-variant-declarator-list:
- struct-or-variant-declarator
- struct-or-variant-declarator-list , struct-or-variant-declarator
- struct-or-variant-declarator:
- declarator
- declarator-opt : unary-expression
- variant-specifier:
- variant identifier-opt variant-tag-opt { struct-or-variant-declaration-list }
- variant identifier variant-tag
- variant-tag:
- < unary-expression >
- enum-specifier:
- enum identifier-opt { enumerator-list }
- enum identifier-opt { enumerator-list , }
- enum identifier
- enum identifier-opt : declaration-specifiers { enumerator-list }
- enum identifier-opt : declaration-specifiers { enumerator-list , }
- enumerator-list:
- enumerator
- enumerator-list , enumerator
- enumerator:
- enumeration-constant
- enumeration-constant assignment-operator unary-expression
- enumeration-constant assignment-operator constant-expression-range
- type-qualifier:
- const
- declarator:
- pointer-opt direct-declarator
- direct-declarator:
- identifier
- ( declarator )
- direct-declarator [ unary-expression ]
- abstract-declarator:
- pointer-opt direct-abstract-declarator
- direct-abstract-declarator:
- identifier-opt
- ( abstract-declarator )
- direct-abstract-declarator [ unary-expression ]
- direct-abstract-declarator [ ]
- pointer:
- * type-qualifier-list-opt
- * type-qualifier-list-opt pointer
- type-qualifier-list:
- type-qualifier
- type-qualifier-list type-qualifier
- typedef-name:
- identifier
- 2.3) CTF-specific declarations
- ctf-specifier:
- clock { ctf-assignment-expression-list-opt }
- event { ctf-assignment-expression-list-opt }
- stream { ctf-assignment-expression-list-opt }
- env { ctf-assignment-expression-list-opt }
- trace { ctf-assignment-expression-list-opt }
- typealias declaration-specifiers abstract-declarator-list type-assignment-operator declaration-specifiers abstract-declarator-list
- typealias declaration-specifiers abstract-declarator-list type-assignment-operator declarator-list
- ctf-type-specifier:
- floating_point { ctf-assignment-expression-list-opt }
- integer { ctf-assignment-expression-list-opt }
- string { ctf-assignment-expression-list-opt }
- string
- ctf-assignment-expression-list:
- ctf-assignment-expression ;
- ctf-assignment-expression-list ctf-assignment-expression ;
- ctf-assignment-expression:
- unary-expression assignment-operator unary-expression
- unary-expression type-assignment-operator type-specifier
- declaration-specifiers-opt storage-class-specifier declaration-specifiers-opt declarator-list
- typealias declaration-specifiers abstract-declarator-list type-assignment-operator declaration-specifiers abstract-declarator-list
- typealias declaration-specifiers abstract-declarator-list type-assignment-operator declarator-list