Fix: default value of `origin-is-unix-epoch` property is `true`
[barectf.git] / barectf / config_parse_v3.py
CommitLineData
4810b707
PP
1# The MIT License (MIT)
2#
3# Copyright (c) 2015-2020 Philippe Proulx <pproulx@efficios.com>
4#
5# Permission is hereby granted, free of charge, to any person obtaining
6# a copy of this software and associated documentation files (the
7# "Software"), to deal in the Software without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish,
9# distribute, sublicense, and/or sell copies of the Software, and to
10# permit persons to whom the Software is furnished to do so, subject to
11# the following conditions:
12#
13# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24import barectf.config_parse_common as barectf_config_parse_common
25from barectf.config_parse_common import _ConfigurationParseError
26from barectf.config_parse_common import _append_error_ctx
2d55dc7d 27from barectf.config_parse_common import _MapNode
4810b707 28import barectf.config as barectf_config
be9f12dc 29from barectf.config import _OptStructFt
4810b707
PP
30import collections
31import uuid
2d55dc7d
PP
32from barectf.typing import Count, Alignment, VersionNumber
33from typing import Optional, List, Dict, Any, TextIO, Set, Iterable, Callable, Tuple, Type
34import typing
4810b707
PP
35
36
37# A barectf 3 YAML configuration parser.
38#
39# When you build such a parser, it parses the configuration node `node`
40# (already loaded from the file having the path `path`) and creates a
41# corresponding `barectf.Configuration` object which you can get with
42# the `config` property.
43#
44# See the comments of _parse() for more implementation details about the
45# parsing stages and general strategy.
46class _Parser(barectf_config_parse_common._Parser):
47 # Builds a barectf 3 YAML configuration parser and parses the root
2d55dc7d
PP
48 # configuration node `node` (already loaded from the file-like
49 # object `root_file`).
50 def __init__(self, root_file: TextIO, node: barectf_config_parse_common._ConfigNodeV3,
51 with_pkg_include_dir: bool, inclusion_dirs: Optional[List[str]],
52 ignore_include_not_found: bool):
53 super().__init__(root_file, node, with_pkg_include_dir, inclusion_dirs,
54 ignore_include_not_found, VersionNumber(3))
be9f12dc
PP
55 self._ft_cls_name_to_create_method: Dict[str, Callable[[_MapNode],
56 List[barectf_config._FieldType]]] = {
4810b707
PP
57 'unsigned-integer': self._create_int_ft,
58 'signed-integer': self._create_int_ft,
59 'unsigned-enumeration': self._create_enum_ft,
60 'signed-enumeration': self._create_enum_ft,
61 'real': self._create_real_ft,
62 'string': self._create_string_ft,
63 'static-array': self._create_static_array_ft,
be9f12dc 64 'dynamic-array': self._create_dynamic_array_ft,
4810b707
PP
65 'structure': self._create_struct_ft,
66 }
67 self._parse()
68
69 # Validates the alignment `alignment`, raising a
70 # `_ConfigurationParseError` exception using `ctx_obj_name` if it's
71 # invalid.
72 @staticmethod
2d55dc7d 73 def _validate_alignment(alignment: Alignment, ctx_obj_name: str):
4810b707
PP
74 assert alignment >= 1
75
76 # check for power of two
77 if (alignment & (alignment - 1)) != 0:
78 raise _ConfigurationParseError(ctx_obj_name,
79 f'Invalid alignment (not a power of two): {alignment}')
80
81 # Validates the TSDL identifier `iden`, raising a
82 # `_ConfigurationParseError` exception using `ctx_obj_name` and
83 # `prop` to format the message if it's invalid.
84 @staticmethod
2d55dc7d 85 def _validate_iden(iden: str, ctx_obj_name: str, prop: str):
4810b707
PP
86 assert type(iden) is str
87 ctf_keywords = {
88 'align',
89 'callsite',
90 'clock',
91 'enum',
92 'env',
93 'event',
94 'floating_point',
95 'integer',
96 'stream',
97 'string',
98 'struct',
99 'trace',
100 'typealias',
101 'typedef',
102 'variant',
103 }
104
105 if iden in ctf_keywords:
106 msg = f'Invalid {prop} (not a valid identifier): `{iden}`'
107 raise _ConfigurationParseError(ctx_obj_name, msg)
108
109 @staticmethod
2d55dc7d 110 def _alignment_prop(ft_node: _MapNode, prop_name: str) -> Alignment:
4810b707
PP
111 alignment = ft_node.get(prop_name)
112
113 if alignment is not None:
114 _Parser._validate_alignment(alignment, '`prop_name` property')
115
2d55dc7d 116 return Alignment(alignment)
4810b707
PP
117
118 @property
2d55dc7d
PP
119 def _trace_type_node(self) -> _MapNode:
120 return self.config_node['trace']['type']
4810b707
PP
121
122 @staticmethod
2d55dc7d 123 def _byte_order_from_node(node: str) -> barectf_config.ByteOrder:
4810b707
PP
124 return {
125 'big-endian': barectf_config.ByteOrder.BIG_ENDIAN,
126 'little-endian': barectf_config.ByteOrder.LITTLE_ENDIAN,
127 }[node]
128
129 # Creates a bit array field type having the type `ft_type` from the
130 # bit array field type node `ft_node`, passing the additional
131 # `*args` to ft_type.__init__().
2d55dc7d
PP
132 def _create_common_bit_array_ft(self, ft_node: _MapNode,
133 ft_type: Type[barectf_config._BitArrayFieldType],
134 default_alignment: Optional[Alignment],
135 *args) -> barectf_config._BitArrayFieldType:
4810b707
PP
136 alignment = self._alignment_prop(ft_node, 'alignment')
137
138 if alignment is None:
139 alignment = default_alignment
140
4c91e769 141 return ft_type(ft_node['size'], alignment, *args)
4810b707
PP
142
143 # Creates an integer field type having the type `ft_type` from the
144 # integer field type node `ft_node`, passing the additional `*args`
145 # to ft_type.__init__().
2d55dc7d
PP
146 def _create_common_int_ft(self, ft_node: _MapNode,
147 ft_type: Type[barectf_config._IntegerFieldType], *args) -> barectf_config._IntegerFieldType:
4810b707
PP
148 preferred_display_base = {
149 'binary': barectf_config.DisplayBase.BINARY,
150 'octal': barectf_config.DisplayBase.OCTAL,
151 'decimal': barectf_config.DisplayBase.DECIMAL,
152 'hexadecimal': barectf_config.DisplayBase.HEXADECIMAL,
153 }[ft_node.get('preferred-display-base', 'decimal')]
2d55dc7d
PP
154 return typing.cast(barectf_config._IntegerFieldType,
155 self._create_common_bit_array_ft(ft_node, ft_type, None,
156 preferred_display_base, *args))
4810b707
PP
157
158 # Creates an integer field type from the unsigned/signed integer
159 # field type node `ft_node`.
be9f12dc 160 def _create_int_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
4810b707
PP
161 ft_type = {
162 'unsigned-integer': barectf_config.UnsignedIntegerFieldType,
163 'signed-integer': barectf_config.SignedIntegerFieldType,
164 }[ft_node['class']]
be9f12dc 165 return [self._create_common_int_ft(ft_node, ft_type)]
4810b707
PP
166
167 # Creates an enumeration field type from the unsigned/signed
168 # enumeration field type node `ft_node`.
be9f12dc 169 def _create_enum_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
4810b707
PP
170 ft_type = {
171 'unsigned-enumeration': barectf_config.UnsignedEnumerationFieldType,
172 'signed-enumeration': barectf_config.SignedEnumerationFieldType,
173 }[ft_node['class']]
174 mappings = collections.OrderedDict()
175
98bc60de 176 for label, mapping_node in ft_node['mappings'].items():
4810b707
PP
177 ranges = set()
178
179 for range_node in mapping_node:
180 if type(range_node) is list:
181 ranges.add(barectf_config.EnumerationFieldTypeMappingRange(range_node[0],
182 range_node[1]))
183 else:
184 assert type(range_node) is int
185 ranges.add(barectf_config.EnumerationFieldTypeMappingRange(range_node,
186 range_node))
187
188 mappings[label] = barectf_config.EnumerationFieldTypeMapping(ranges)
189
be9f12dc
PP
190 return [typing.cast(barectf_config._EnumerationFieldType,
191 self._create_common_int_ft(ft_node, ft_type,
192 barectf_config.EnumerationFieldTypeMappings(mappings)))]
4810b707
PP
193
194 # Creates a real field type from the real field type node `ft_node`.
be9f12dc
PP
195 def _create_real_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
196 return [typing.cast(barectf_config.RealFieldType,
197 self._create_common_bit_array_ft(ft_node, barectf_config.RealFieldType,
198 Alignment(8)))]
4810b707
PP
199
200 # Creates a string field type from the string field type node
201 # `ft_node`.
be9f12dc
PP
202 def _create_string_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
203 return [barectf_config.StringFieldType()]
4810b707 204
be9f12dc 205 def _create_array_ft(self, ft_type, ft_node: _MapNode, **kwargs) -> barectf_config._ArrayFieldType:
4810b707
PP
206 prop_name = 'element-field-type'
207
208 try:
be9f12dc 209 element_fts = self._create_fts(ft_node[prop_name])
4810b707
PP
210 except _ConfigurationParseError as exc:
211 _append_error_ctx(exc, f'`{prop_name}` property')
212
be9f12dc
PP
213 if len(element_fts) != 1 or isinstance(element_fts[0], (barectf_config.StructureFieldType,
214 barectf_config.DynamicArrayFieldType)):
215 raise _ConfigurationParseError(f'`{prop_name}` property',
216 'Nested structure and dynamic array field types are not supported')
217
218 return ft_type(element_field_type=element_fts[0], **kwargs)
219
220 # Creates a static array field type from the static array field type
221 # node `ft_node`.
222 def _create_static_array_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
223 return [typing.cast(barectf_config.StaticArrayFieldType,
224 self._create_array_ft(barectf_config.StaticArrayFieldType, ft_node,
225 length=ft_node['length']))]
226
227 # Creates a dynamic array field type from the dynamic array field
228 # type node `ft_node`.
229 def _create_dynamic_array_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
230 # create length unsigned integer field type
231 len_ft = barectf_config.UnsignedIntegerFieldType(32, alignment=Alignment(8))
232 return [
233 len_ft,
234 typing.cast(barectf_config.DynamicArrayFieldType,
235 self._create_array_ft(barectf_config.DynamicArrayFieldType, ft_node,
236 length_field_type=len_ft))
237 ]
4810b707
PP
238
239 # Creates structure field type members from the structure field type
240 # members node `members_node`.
241 #
242 # `prop_name` is the name of the property of which `members_node` is
243 # the value.
2d55dc7d 244 def _create_struct_ft_members(self, members_node: List[_MapNode], prop_name: str):
4810b707 245 members = collections.OrderedDict()
2d55dc7d 246 member_names: Set[str] = set()
4810b707
PP
247
248 for member_node in members_node:
249 member_name, member_node = list(member_node.items())[0]
250
251 if member_name in member_names:
252 raise _ConfigurationParseError(f'`{prop_name}` property',
253 f'Duplicate member `{member_name}`')
254
255 self._validate_iden(member_name, f'`{prop_name}` property',
256 'structure field type member name')
257 member_names.add(member_name)
258 ft_prop_name = 'field-type'
259 ft_node = member_node[ft_prop_name]
260
261 try:
6bc97055 262 if ft_node['class'] in ['structure']:
4810b707 263 raise _ConfigurationParseError(f'`{ft_prop_name}` property',
6bc97055 264 'Nested structure field types are not supported')
4810b707
PP
265
266 try:
be9f12dc 267 member_fts = self._create_fts(ft_node)
4810b707 268 except _ConfigurationParseError as exc:
be9f12dc 269 _append_error_ctx(exc, f'`{ft_prop_name}` property')
4810b707
PP
270 except _ConfigurationParseError as exc:
271 _append_error_ctx(exc, f'Structure field type member `{member_name}`')
272
be9f12dc
PP
273 if len(member_fts) == 2:
274 # The only case where this happens is a dynamic array
275 # field type node which generates an unsigned integer
276 # field type for the length and the dynamic array field
277 # type itself.
278 assert type(member_fts[1]) is barectf_config.DynamicArrayFieldType
279 members[f'__{member_name}_len'] = barectf_config.StructureFieldTypeMember(member_fts[0])
280 else:
281 assert len(member_fts) == 1
282
283 members[member_name] = barectf_config.StructureFieldTypeMember(member_fts[-1])
4810b707
PP
284
285 return barectf_config.StructureFieldTypeMembers(members)
286
287 # Creates a structure field type from the structure field type node
288 # `ft_node`.
be9f12dc 289 def _create_struct_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
4810b707
PP
290 minimum_alignment = self._alignment_prop(ft_node, 'minimum-alignment')
291
292 if minimum_alignment is None:
293 minimum_alignment = 1
294
295 members = None
296 prop_name = 'members'
297 members_node = ft_node.get(prop_name)
298
299 if members_node is not None:
300 members = self._create_struct_ft_members(members_node, prop_name)
301
be9f12dc 302 return [barectf_config.StructureFieldType(minimum_alignment, members)]
4810b707 303
be9f12dc
PP
304 # Creates field types from the field type node `ft_node`.
305 def _create_fts(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
4810b707
PP
306 return self._ft_cls_name_to_create_method[ft_node['class']](ft_node)
307
be9f12dc 308 # Creates field types from the field type node `parent_node[key]`
4810b707 309 # if it exists.
be9f12dc 310 def _try_create_fts(self, parent_node: _MapNode, key: str) -> Optional[List[barectf_config._FieldType]]:
4810b707 311 if key not in parent_node:
2d55dc7d 312 return None
4810b707
PP
313
314 try:
be9f12dc 315 return self._create_fts(parent_node[key])
4810b707
PP
316 except _ConfigurationParseError as exc:
317 _append_error_ctx(exc, f'`{key}` property')
318
2d55dc7d
PP
319 # satisfy static type checker (never reached)
320 raise
321
be9f12dc
PP
322 # Like _try_create_fts(), but casts the result's type (first and
323 # only element) to `barectf_config.StructureFieldType` to satisfy
324 # static type checkers.
2d55dc7d 325 def _try_create_struct_ft(self, parent_node: _MapNode, key: str) -> _OptStructFt:
be9f12dc
PP
326 fts = self._try_create_fts(parent_node, key)
327
328 if fts is None:
329 return None
330
331 return typing.cast(barectf_config.StructureFieldType, fts[0])
2d55dc7d 332
4810b707
PP
333 # Returns the total number of members in the structure field type
334 # node `ft_node` if it exists, otherwise 0.
335 @staticmethod
2d55dc7d 336 def _total_struct_ft_node_members(ft_node: Optional[_MapNode]) -> Count:
4810b707 337 if ft_node is None:
2d55dc7d 338 return Count(0)
4810b707
PP
339
340 members_node = ft_node.get('members')
341
342 if members_node is None:
2d55dc7d 343 return Count(0)
4810b707 344
2d55dc7d 345 return Count(len(members_node))
4810b707 346
e8f0d548
PP
347 # Creates an event record type from the event record type node
348 # `ert_node` named `name`.
4810b707 349 #
e8f0d548
PP
350 # `ert_member_count` is the total number of structure field type
351 # members within the event record type so far (from the common part
352 # in its data stream type). For example, if the data stream type has
353 # an event record header field type with `id` and `timestamp`
354 # members, then `ert_member_count` is 2.
355 def _create_ert(self, name: str, ert_node: _MapNode,
356 ert_member_count: Count) -> barectf_config.EventRecordType:
4810b707 357 try:
e8f0d548 358 self._validate_iden(name, '`name` property', 'event record type name')
4810b707 359
e8f0d548 360 # make sure the event record type is not empty
4810b707
PP
361 spec_ctx_ft_prop_name = 'specific-context-field-type'
362 payload_ft_prop_name = 'payload-field-type'
e8f0d548
PP
363 ert_member_count = Count(ert_member_count + self._total_struct_ft_node_members(ert_node.get(spec_ctx_ft_prop_name)))
364 ert_member_count = Count(ert_member_count + self._total_struct_ft_node_members(ert_node.get(payload_ft_prop_name)))
4810b707 365
e8f0d548
PP
366 if ert_member_count == 0:
367 raise _ConfigurationParseError('Event record type',
368 'Event record type is empty (no members).')
4810b707 369
e8f0d548
PP
370 # create event record type
371 return barectf_config.EventRecordType(name, ert_node.get('log-level'),
372 self._try_create_struct_ft(ert_node,
2d55dc7d 373 spec_ctx_ft_prop_name),
e8f0d548 374 self._try_create_struct_ft(ert_node,
2d55dc7d 375 payload_ft_prop_name))
4810b707 376 except _ConfigurationParseError as exc:
e8f0d548 377 _append_error_ctx(exc, f'Event record type `{name}`')
4810b707 378
2d55dc7d
PP
379 # satisfy static type checker (never reached)
380 raise
381
4810b707
PP
382 # Returns the effective feature field type for the field type
383 # node `parent_node[key]`, if any.
384 #
385 # Returns:
386 #
387 # If `parent_node[key]` is `False`:
388 # `None`.
389 #
390 # If `parent_node[key]` is `True`:
391 # `barectf_config.DEFAULT_FIELD_TYPE`.
392 #
393 # If `parent_node[key]` doesn't exist:
394 # `none` (parameter).
395 #
396 # Otherwise:
397 # A created field type.
2d55dc7d 398 def _feature_ft(self, parent_node: _MapNode, key: str, none: Any = None) -> Any:
4810b707
PP
399 if key not in parent_node:
400 # missing: default feature field type
401 return none
402
403 ft_node = parent_node[key]
404 assert ft_node is not None
405
406 if ft_node is True:
407 # default feature field type
408 return barectf_config.DEFAULT_FIELD_TYPE
409
410 if ft_node is False:
411 # disabled feature
412 return None
413
414 assert type(ft_node) is collections.OrderedDict
be9f12dc 415 return self._create_fts(ft_node)[0]
4810b707 416
e8f0d548 417 def _create_dst(self, name: str, dst_node: _MapNode) -> barectf_config.DataStreamType:
4810b707 418 try:
e8f0d548
PP
419 # validate data stream type's name
420 self._validate_iden(name, '`name` property', 'data stream type name')
4810b707
PP
421
422 # get default clock type, if any
423 def_clk_type = None
424 prop_name = '$default-clock-type-name'
e8f0d548 425 def_clk_type_name = dst_node.get(prop_name)
4810b707
PP
426
427 if def_clk_type_name is not None:
428 try:
429 def_clk_type = self._clk_type(def_clk_type_name, prop_name)
430 except _ConfigurationParseError as exc:
431 _append_error_ctx(exc, f'`{prop_name}` property')
432
433 # create feature field types
434 pkt_total_size_ft = barectf_config.DEFAULT_FIELD_TYPE
435 pkt_content_size_ft = barectf_config.DEFAULT_FIELD_TYPE
462e49b3
PP
436 pkt_beginning_ts_ft = None
437 pkt_end_ts_ft = None
470ff4d9 438 pkt_disc_er_counter_snap_ft = barectf_config.DEFAULT_FIELD_TYPE
e8f0d548 439 ert_id_ft = barectf_config.DEFAULT_FIELD_TYPE
462e49b3 440 ert_ts_ft = None
af09c4fc 441 pkt_seq_num_ft = None
4810b707
PP
442
443 if def_clk_type is not None:
e8f0d548 444 # The data stream type has a default clock type.
462e49b3
PP
445 # Initialize the packet beginning timestamp, packet end
446 # timestamp, and event record timestamp field types to
447 # default field types.
4810b707 448 #
e8f0d548
PP
449 # This means your data stream type node only needs a
450 # default clock type name to enable those features
4810b707 451 # automatically. Those features do not add any parameter
e8f0d548 452 # to the event tracing functions.
462e49b3
PP
453 pkt_beginning_ts_ft = barectf_config.DEFAULT_FIELD_TYPE
454 pkt_end_ts_ft = barectf_config.DEFAULT_FIELD_TYPE
455 ert_ts_ft = barectf_config.DEFAULT_FIELD_TYPE
4810b707 456
e8f0d548 457 features_node = dst_node.get('$features')
4810b707
PP
458
459 if features_node is not None:
460 # create packet feature field types
461 pkt_node = features_node.get('packet')
462
463 if pkt_node is not None:
464 pkt_total_size_ft = self._feature_ft(pkt_node, 'total-size-field-type',
465 pkt_total_size_ft)
466 pkt_content_size_ft = self._feature_ft(pkt_node, 'content-size-field-type',
467 pkt_content_size_ft)
462e49b3
PP
468 pkt_beginning_ts_ft = self._feature_ft(pkt_node,
469 'beginning-timestamp-field-type',
470 pkt_beginning_ts_ft)
471 pkt_end_ts_ft = self._feature_ft(pkt_node, 'end-timestamp-field-type',
472 pkt_end_ts_ft)
470ff4d9 473 pkt_disc_er_counter_snap_ft = self._feature_ft(pkt_node,
e8f0d548 474 'discarded-event-records-counter-snapshot-field-type',
470ff4d9 475 pkt_disc_er_counter_snap_ft)
af09c4fc
JL
476 pkt_seq_num_ft = self._feature_ft(pkt_node, 'sequence-number-field-type',
477 pkt_seq_num_ft)
4810b707 478
e8f0d548
PP
479 # create event record feature field types
480 er_node = features_node.get('event-record')
4810b707
PP
481 type_id_ft_prop_name = 'type-id-field-type'
482
e8f0d548
PP
483 if er_node is not None:
484 ert_id_ft = self._feature_ft(er_node, type_id_ft_prop_name, ert_id_ft)
462e49b3 485 ert_ts_ft = self._feature_ft(er_node, 'timestamp-field-type', ert_ts_ft)
4810b707 486
e8f0d548
PP
487 erts_prop_name = 'event-record-types'
488 ert_count = len(dst_node[erts_prop_name])
4810b707
PP
489
490 try:
e8f0d548 491 if ert_id_ft is None and ert_count > 1:
4810b707 492 raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property',
e8f0d548 493 'Event record type ID field type feature is required because data stream type has more than one event record type')
4810b707 494
e8f0d548
PP
495 if isinstance(ert_id_ft, barectf_config._IntegerFieldType):
496 ert_id_int_ft = typing.cast(barectf_config._IntegerFieldType, ert_id_ft)
2d55dc7d 497
e8f0d548 498 if ert_count > (1 << ert_id_int_ft.size):
2d55dc7d 499 raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property',
e8f0d548 500 f'Field type\'s size ({ert_id_int_ft.size} bits) is too small to accomodate {ert_count} event record types')
4810b707 501 except _ConfigurationParseError as exc:
e8f0d548 502 exc._append_ctx('`event-record` property')
4810b707
PP
503 _append_error_ctx(exc, '`$features` property')
504
e8f0d548
PP
505 pkt_features = barectf_config.DataStreamTypePacketFeatures(pkt_total_size_ft,
506 pkt_content_size_ft,
462e49b3
PP
507 pkt_beginning_ts_ft,
508 pkt_end_ts_ft,
af09c4fc
JL
509 pkt_disc_er_counter_snap_ft,
510 pkt_seq_num_ft)
462e49b3 511 er_features = barectf_config.DataStreamTypeEventRecordFeatures(ert_id_ft, ert_ts_ft)
e8f0d548 512 features = barectf_config.DataStreamTypeFeatures(pkt_features, er_features)
4810b707
PP
513
514 # create packet context (structure) field type extra members
515 pkt_ctx_ft_extra_members = None
516 prop_name = 'packet-context-field-type-extra-members'
e8f0d548 517 pkt_ctx_ft_extra_members_node = dst_node.get(prop_name)
4810b707
PP
518
519 if pkt_ctx_ft_extra_members_node is not None:
520 pkt_ctx_ft_extra_members = self._create_struct_ft_members(pkt_ctx_ft_extra_members_node,
521 prop_name)
522
523 # check for illegal packet context field type member names
524 reserved_member_names = {
525 'packet_size',
526 'content_size',
527 'timestamp_begin',
528 'timestamp_end',
529 'events_discarded',
530 'packet_seq_num',
531 }
532
533 for member_name in pkt_ctx_ft_extra_members:
534 if member_name in reserved_member_names:
535 raise _ConfigurationParseError(f'`{prop_name}` property',
536 f'Packet context field type member name `{member_name}` is reserved.')
537
e8f0d548
PP
538 # create event record types
539 er_header_common_ctx_member_count = Count(0)
4810b707 540
e8f0d548
PP
541 if er_features.type_id_field_type is not None:
542 er_header_common_ctx_member_count = Count(er_header_common_ctx_member_count + 1)
4810b707 543
462e49b3 544 if er_features.timestamp_field_type is not None:
e8f0d548 545 er_header_common_ctx_member_count = Count(er_header_common_ctx_member_count + 1)
4810b707 546
e8f0d548
PP
547 er_common_ctx_ft_prop_name = 'event-record-common-context-field-type'
548 er_common_ctx_ft_node = dst_node.get(er_common_ctx_ft_prop_name)
549 er_header_common_ctx_member_count = Count(er_header_common_ctx_member_count + self._total_struct_ft_node_members(er_common_ctx_ft_node))
550 erts = set()
4810b707 551
e8f0d548
PP
552 for ert_name, ert_node in dst_node[erts_prop_name].items():
553 erts.add(self._create_ert(ert_name, ert_node, er_header_common_ctx_member_count))
4810b707 554
e8f0d548
PP
555 # create data stream type
556 return barectf_config.DataStreamType(name, erts, def_clk_type, features,
557 pkt_ctx_ft_extra_members,
558 self._try_create_struct_ft(dst_node,
559 er_common_ctx_ft_prop_name))
4810b707 560 except _ConfigurationParseError as exc:
e8f0d548 561 _append_error_ctx(exc, f'Data data stream type `{name}`')
4810b707 562
2d55dc7d
PP
563 # satisfy static type checker (never reached)
564 raise
565
566 def _clk_type(self, name: str, prop_name: str) -> barectf_config.ClockType:
4810b707
PP
567 clk_type = self._clk_types.get(name)
568
569 if clk_type is None:
570 raise _ConfigurationParseError(f'`{prop_name}` property',
571 f'Clock type `{name}` does not exist')
572
573 return clk_type
574
2d55dc7d 575 def _create_clk_type(self, name: str, clk_type_node: _MapNode) -> barectf_config.ClockType:
4810b707
PP
576 self._validate_iden(name, '`name` property', 'clock type name')
577 clk_type_uuid = None
578 uuid_node = clk_type_node.get('uuid')
579
580 if uuid_node is not None:
581 clk_type_uuid = uuid.UUID(uuid_node)
582
583 offset_seconds = 0
2d55dc7d 584 offset_cycles = Count(0)
4810b707
PP
585 offset_node = clk_type_node.get('offset')
586
587 if offset_node is not None:
588 offset_seconds = offset_node.get('seconds', 0)
2d55dc7d 589 offset_cycles = offset_node.get('cycles', Count(0))
4810b707
PP
590
591 return barectf_config.ClockType(name, clk_type_node.get('frequency', int(1e9)),
592 clk_type_uuid, clk_type_node.get('description'),
593 clk_type_node.get('precision', 0),
594 barectf_config.ClockTypeOffset(offset_seconds, offset_cycles),
21910b4f 595 clk_type_node.get('origin-is-unix-epoch', True))
4810b707
PP
596
597 def _create_clk_types(self):
598 self._clk_types = {}
599
600 for clk_type_name, clk_type_node in self._trace_type_node.get('clock-types', {}).items():
601 self._clk_types[clk_type_name] = self._create_clk_type(clk_type_name, clk_type_node)
602
603 def _create_trace_type(self):
604 try:
e8f0d548 605 # create clock types (_create_dst() needs them)
4810b707
PP
606 self._create_clk_types()
607
608 # get UUID
609 trace_type_uuid = None
610 uuid_node = self._trace_type_node.get('uuid')
611
612 if uuid_node is not None:
613 if uuid_node == 'auto':
614 trace_type_uuid = uuid.uuid1()
615 else:
616 trace_type_uuid = uuid.UUID(uuid_node)
617
618 # create feature field types
619 magic_ft = barectf_config.DEFAULT_FIELD_TYPE
620 uuid_ft = None
e8f0d548 621 dst_id_ft = barectf_config.DEFAULT_FIELD_TYPE
4810b707
PP
622
623 if trace_type_uuid is not None:
624 # Trace type has a UUID: initialize UUID field type to
625 # a default field type.
626 uuid_ft = barectf_config.DEFAULT_FIELD_TYPE
627
628 features_node = self._trace_type_node.get('$features')
e8f0d548 629 dst_id_ft_prop_name = 'data-stream-type-id-field-type'
4810b707
PP
630
631 if features_node is not None:
632 magic_ft = self._feature_ft(features_node, 'magic-field-type',
633 magic_ft)
634 uuid_ft = self._feature_ft(features_node, 'uuid-field-type', uuid_ft)
e8f0d548 635 dst_id_ft = self._feature_ft(features_node, dst_id_ft_prop_name, dst_id_ft)
4810b707 636
e8f0d548
PP
637 dsts_prop_name = 'data-stream-types'
638 dst_count = len(self._trace_type_node[dsts_prop_name])
4810b707
PP
639
640 try:
e8f0d548
PP
641 if dst_id_ft is None and dst_count > 1:
642 raise _ConfigurationParseError(f'`{dst_id_ft_prop_name}` property',
643 'Data stream type ID field type feature is required because trace type has more than one data stream type')
4810b707 644
e8f0d548
PP
645 if isinstance(dst_id_ft, barectf_config._FieldType) and dst_count > (1 << dst_id_ft.size):
646 raise _ConfigurationParseError(f'`{dst_id_ft_prop_name}` property',
647 f'Field type\'s size ({dst_id_ft.size} bits) is too small to accomodate {dst_count} data stream types')
4810b707
PP
648 except _ConfigurationParseError as exc:
649 _append_error_ctx(exc, '`$features` property')
650
e8f0d548 651 features = barectf_config.TraceTypeFeatures(magic_ft, uuid_ft, dst_id_ft)
4810b707 652
e8f0d548
PP
653 # create data stream types
654 dsts = set()
4810b707 655
e8f0d548
PP
656 for dst_name, dst_node in self._trace_type_node[dsts_prop_name].items():
657 dsts.add(self._create_dst(dst_name, dst_node))
4810b707
PP
658
659 # create trace type
313a0bd9 660 return barectf_config.TraceType(self._native_byte_order, dsts, trace_type_uuid,
b2c06e26 661 features)
4810b707
PP
662 except _ConfigurationParseError as exc:
663 _append_error_ctx(exc, 'Trace type')
664
665 def _create_trace(self):
666 try:
667 trace_type = self._create_trace_type()
2d55dc7d 668 trace_node = self.config_node['trace']
4810b707
PP
669 env = None
670 env_node = trace_node.get('environment')
671
672 if env_node is not None:
673 # validate each environment variable name
674 for name in env_node:
675 self._validate_iden(name, '`environment` property',
676 'environment variable name')
677
678 # the node already has the expected structure
679 env = barectf_config.TraceEnvironment(env_node)
680
681 return barectf_config.Trace(trace_type, env)
682
683 except _ConfigurationParseError as exc:
684 _append_error_ctx(exc, 'Trace')
685
686 def _create_config(self):
687 # create trace first
688 trace = self._create_trace()
689
e8f0d548
PP
690 # find default data stream type, if any
691 def_dst = None
4810b707 692
e8f0d548 693 for dst_name, dst_node in self._trace_type_node['data-stream-types'].items():
4810b707 694 prop_name = '$is-default'
e8f0d548 695 is_default = dst_node.get(prop_name)
4810b707
PP
696
697 if is_default is True:
e8f0d548 698 if def_dst is not None:
4810b707 699 exc = _ConfigurationParseError(f'`{prop_name}` property',
e8f0d548
PP
700 f'Duplicate default data stream type (`{def_dst.name}`)')
701 exc._append_ctx(f'Data stream type `{dst_name}`')
4810b707
PP
702 _append_error_ctx(exc, 'Trace type')
703
e8f0d548 704 def_dst = trace.type.data_stream_type(dst_name)
4810b707
PP
705
706 # create clock type C type mapping
707 clk_types_node = self._trace_type_node.get('clock-types')
708 clk_type_c_types = None
709
710 if clk_types_node is not None:
711 clk_type_c_types = collections.OrderedDict()
712
e8f0d548
PP
713 for dst in trace.type.data_stream_types:
714 if dst.default_clock_type is None:
4810b707
PP
715 continue
716
e8f0d548 717 clk_type_node = clk_types_node[dst.default_clock_type.name]
4810b707
PP
718 c_type = clk_type_node.get('$c-type')
719
720 if c_type is not None:
e8f0d548 721 clk_type_c_types[dst.default_clock_type] = c_type
4810b707
PP
722
723 # create options
724 iden_prefix_def = False
e8f0d548 725 def_dst_name_def = False
2d55dc7d
PP
726 opts_node = self.config_node.get('options')
727 iden_prefix = 'barectf_'
728 file_name_prefix = 'barectf'
4810b707
PP
729
730 if opts_node is not None:
731 code_gen_opts_node = opts_node.get('code-generation')
732
733 if code_gen_opts_node is not None:
734 prefix_node = code_gen_opts_node.get('prefix', 'barectf')
735
736 if type(prefix_node) is str:
737 # automatic prefixes
738 iden_prefix = f'{prefix_node}_'
739 file_name_prefix = prefix_node
740 else:
741 iden_prefix = prefix_node['identifier']
742 file_name_prefix = prefix_node['file-name']
743
744 header_opts = code_gen_opts_node.get('header')
745
746 if header_opts is not None:
747 iden_prefix_def = header_opts.get('identifier-prefix-definition', False)
e8f0d548
PP
748 def_dst_name_def = header_opts.get('default-data-stream-type-name-definition',
749 False)
4810b707
PP
750
751 header_opts = barectf_config.ConfigurationCodeGenerationHeaderOptions(iden_prefix_def,
e8f0d548 752 def_dst_name_def)
4810b707 753 cg_opts = barectf_config.ConfigurationCodeGenerationOptions(iden_prefix, file_name_prefix,
e8f0d548 754 def_dst, header_opts,
4810b707
PP
755 clk_type_c_types)
756 opts = barectf_config.ConfigurationOptions(cg_opts)
757
758 # create configuration
b2c06e26 759 self._config = barectf_config.Configuration(trace, opts)
4810b707
PP
760
761 # Expands the field type aliases found in the trace type node.
762 #
763 # This method modifies the trace type node.
764 #
765 # When this method returns:
766 #
767 # * Any field type alias is replaced with its full field type
768 # node equivalent.
769 #
770 # * The `$field-type-aliases` property of the trace type node is
771 # removed.
772 def _expand_ft_aliases(self):
2d55dc7d 773 def resolve_ft_alias_from(parent_node: _MapNode, key: str):
4810b707
PP
774 if key not in parent_node:
775 return
776
777 if type(parent_node[key]) not in [collections.OrderedDict, str]:
778 return
779
2d55dc7d 780 self._resolve_ft_alias_from(ft_aliases_node, parent_node, key)
4810b707
PP
781
782 ft_aliases_node = self._trace_type_node['$field-type-aliases']
783
e8f0d548
PP
784 # Expand field type aliases within trace, data stream, and event
785 # record type nodes.
4810b707
PP
786 features_prop_name = '$features'
787
788 try:
789 features_node = self._trace_type_node.get(features_prop_name)
790
791 if features_node is not None:
792 try:
793 resolve_ft_alias_from(features_node, 'magic-field-type')
794 resolve_ft_alias_from(features_node, 'uuid-field-type')
e8f0d548 795 resolve_ft_alias_from(features_node, 'data-stream-type-id-field-type')
4810b707
PP
796 except _ConfigurationParseError as exc:
797 _append_error_ctx(exc, f'`{features_prop_name}` property')
798 except _ConfigurationParseError as exc:
799 _append_error_ctx(exc, 'Trace type')
800
e8f0d548 801 for dst_name, dst_node in self._trace_type_node['data-stream-types'].items():
4810b707 802 try:
e8f0d548 803 features_node = dst_node.get(features_prop_name)
4810b707
PP
804
805 if features_node is not None:
806 try:
807 pkt_prop_name = 'packet'
808 pkt_node = features_node.get(pkt_prop_name)
809
810 if pkt_node is not None:
811 try:
812 resolve_ft_alias_from(pkt_node, 'total-size-field-type')
813 resolve_ft_alias_from(pkt_node, 'content-size-field-type')
462e49b3
PP
814 resolve_ft_alias_from(pkt_node, 'beginning-timestamp-field-type')
815 resolve_ft_alias_from(pkt_node, 'end-timestamp-field-type')
4810b707 816 resolve_ft_alias_from(pkt_node,
e8f0d548 817 'discarded-event-records-counter-snapshot-field-type')
af09c4fc 818 resolve_ft_alias_from(pkt_node, 'sequence-number-field-type')
4810b707
PP
819 except _ConfigurationParseError as exc:
820 _append_error_ctx(exc, f'`{pkt_prop_name}` property')
821
e8f0d548
PP
822 er_prop_name = 'event-record'
823 er_node = features_node.get(er_prop_name)
4810b707 824
e8f0d548 825 if er_node is not None:
4810b707 826 try:
e8f0d548 827 resolve_ft_alias_from(er_node, 'type-id-field-type')
462e49b3 828 resolve_ft_alias_from(er_node, 'timestamp-field-type')
4810b707 829 except _ConfigurationParseError as exc:
e8f0d548 830 _append_error_ctx(exc, f'`{er_prop_name}` property')
4810b707
PP
831 except _ConfigurationParseError as exc:
832 _append_error_ctx(exc, f'`{features_prop_name}` property')
833
834 pkt_ctx_ft_extra_members_prop_name = 'packet-context-field-type-extra-members'
e8f0d548 835 pkt_ctx_ft_extra_members_node = dst_node.get(pkt_ctx_ft_extra_members_prop_name)
4810b707
PP
836
837 if pkt_ctx_ft_extra_members_node is not None:
838 try:
839 for member_node in pkt_ctx_ft_extra_members_node:
840 member_node = list(member_node.values())[0]
841 resolve_ft_alias_from(member_node, 'field-type')
842 except _ConfigurationParseError as exc:
843 _append_error_ctx(exc, f'`{pkt_ctx_ft_extra_members_prop_name}` property')
844
e8f0d548 845 resolve_ft_alias_from(dst_node, 'event-record-common-context-field-type')
4810b707 846
e8f0d548 847 for ert_name, ert_node in dst_node['event-record-types'].items():
4810b707 848 try:
e8f0d548
PP
849 resolve_ft_alias_from(ert_node, 'specific-context-field-type')
850 resolve_ft_alias_from(ert_node, 'payload-field-type')
4810b707 851 except _ConfigurationParseError as exc:
e8f0d548 852 _append_error_ctx(exc, f'Event record type `{ert_name}`')
4810b707 853 except _ConfigurationParseError as exc:
e8f0d548 854 _append_error_ctx(exc, f'Data stream type `{dst_name}`')
4810b707
PP
855
856 # remove the (now unneeded) `$field-type-aliases` property
857 del self._trace_type_node['$field-type-aliases']
858
859 # Applies field type inheritance to all field type nodes found in
860 # the trace type node.
861 #
862 # This method modifies the trace type node.
863 #
864 # When this method returns, no field type node has an `$inherit`
865 # property.
866 def _apply_fts_inheritance(self):
2d55dc7d 867 def apply_ft_inheritance(parent_node: _MapNode, key: str):
4810b707
PP
868 if key not in parent_node:
869 return
870
871 if type(parent_node[key]) is not collections.OrderedDict:
872 return
873
2d55dc7d 874 self._apply_ft_inheritance(parent_node, key)
4810b707
PP
875
876 features_prop_name = '$features'
877 features_node = self._trace_type_node.get(features_prop_name)
878
879 if features_node is not None:
880 apply_ft_inheritance(features_node, 'magic-field-type')
881 apply_ft_inheritance(features_node, 'uuid-field-type')
e8f0d548 882 apply_ft_inheritance(features_node, 'data-stream-type-id-field-type')
4810b707 883
e8f0d548
PP
884 for dst_node in self._trace_type_node['data-stream-types'].values():
885 features_node = dst_node.get(features_prop_name)
4810b707
PP
886
887 if features_node is not None:
888 pkt_node = features_node.get('packet')
889
890 if pkt_node is not None:
891 apply_ft_inheritance(pkt_node, 'total-size-field-type')
892 apply_ft_inheritance(pkt_node, 'content-size-field-type')
462e49b3
PP
893 apply_ft_inheritance(pkt_node, 'beginning-timestamp-field-type')
894 apply_ft_inheritance(pkt_node, 'end-timestamp-field-type')
895 apply_ft_inheritance(pkt_node,
896 'discarded-event-records-counter-snapshot-field-type')
af09c4fc 897 apply_ft_inheritance(pkt_node, 'sequence-number-field-type')
4810b707 898
e8f0d548 899 er_node = features_node.get('event-record')
4810b707 900
e8f0d548
PP
901 if er_node is not None:
902 apply_ft_inheritance(er_node, 'type-id-field-type')
462e49b3 903 apply_ft_inheritance(er_node, 'timestamp-field-type')
4810b707 904
e8f0d548 905 pkt_ctx_ft_extra_members_node = dst_node.get('packet-context-field-type-extra-members')
4810b707
PP
906
907 if pkt_ctx_ft_extra_members_node is not None:
908 for member_node in pkt_ctx_ft_extra_members_node:
909 member_node = list(member_node.values())[0]
910 apply_ft_inheritance(member_node, 'field-type')
911
e8f0d548 912 apply_ft_inheritance(dst_node, 'event-record-common-context-field-type')
4810b707 913
e8f0d548
PP
914 for ert_node in dst_node['event-record-types'].values():
915 apply_ft_inheritance(ert_node, 'specific-context-field-type')
916 apply_ft_inheritance(ert_node, 'payload-field-type')
4810b707
PP
917
918 # Normalizes structure field type member nodes.
919 #
920 # A structure field type member node can look like this:
921 #
922 # - msg: custom-string
923 #
924 # which is the equivalent of this:
925 #
926 # - msg:
927 # field-type: custom-string
928 #
929 # This method normalizes form 1 to use form 2.
930 def _normalize_struct_ft_member_nodes(self):
2d55dc7d 931 def normalize_members_node(members_node: List[_MapNode]):
4810b707
PP
932 ft_prop_name = 'field-type'
933
934 for member_node in members_node:
935 member_name, val_node = list(member_node.items())[0]
936
937 if type(val_node) is str:
938 member_node[member_name] = collections.OrderedDict({
939 ft_prop_name: val_node
940 })
941
942 normalize_struct_ft_member_nodes(member_node[member_name], ft_prop_name)
943
2d55dc7d 944 def normalize_struct_ft_member_nodes(parent_node: _MapNode, key: str):
4810b707
PP
945 if type(parent_node) is not collections.OrderedDict:
946 return
947
948 ft_node = parent_node.get(key)
949
950 if type(ft_node) is not collections.OrderedDict:
951 return
952
2d55dc7d 953 ft_node = typing.cast(collections.OrderedDict, ft_node)
4810b707
PP
954 members_nodes = ft_node.get('members')
955
956 if members_nodes is not None:
957 normalize_members_node(members_nodes)
958
959 prop_name = '$field-type-aliases'
960 ft_aliases_node = self._trace_type_node.get(prop_name)
961
962 if ft_aliases_node is not None:
963 for alias in ft_aliases_node:
964 normalize_struct_ft_member_nodes(ft_aliases_node, alias)
965
966 features_prop_name = '$features'
967 features_node = self._trace_type_node.get(features_prop_name)
968
969 if features_node is not None:
970 normalize_struct_ft_member_nodes(features_node, 'magic-field-type')
971 normalize_struct_ft_member_nodes(features_node, 'uuid-field-type')
e8f0d548 972 normalize_struct_ft_member_nodes(features_node, 'data-stream-type-id-field-type')
4810b707 973
e8f0d548
PP
974 for dst_node in self._trace_type_node['data-stream-types'].values():
975 features_node = dst_node.get(features_prop_name)
4810b707
PP
976
977 if features_node is not None:
978 pkt_node = features_node.get('packet')
979
980 if pkt_node is not None:
981 normalize_struct_ft_member_nodes(pkt_node, 'total-size-field-type')
982 normalize_struct_ft_member_nodes(pkt_node, 'content-size-field-type')
462e49b3
PP
983 normalize_struct_ft_member_nodes(pkt_node, 'beginning-timestamp-field-type')
984 normalize_struct_ft_member_nodes(pkt_node, 'end-timestamp-field-type')
4810b707 985 normalize_struct_ft_member_nodes(pkt_node,
e8f0d548 986 'discarded-event-records-counter-snapshot-field-type')
af09c4fc 987 normalize_struct_ft_member_nodes(pkt_node, 'sequence-number-field-type')
4810b707 988
e8f0d548 989 er_node = features_node.get('event-record')
4810b707 990
e8f0d548
PP
991 if er_node is not None:
992 normalize_struct_ft_member_nodes(er_node, 'type-id-field-type')
462e49b3 993 normalize_struct_ft_member_nodes(er_node, 'timestamp-field-type')
4810b707 994
e8f0d548 995 pkt_ctx_ft_extra_members_node = dst_node.get('packet-context-field-type-extra-members')
4810b707
PP
996
997 if pkt_ctx_ft_extra_members_node is not None:
998 normalize_members_node(pkt_ctx_ft_extra_members_node)
999
e8f0d548 1000 normalize_struct_ft_member_nodes(dst_node, 'event-record-common-context-field-type')
4810b707 1001
e8f0d548
PP
1002 for ert_node in dst_node['event-record-types'].values():
1003 normalize_struct_ft_member_nodes(ert_node, 'specific-context-field-type')
1004 normalize_struct_ft_member_nodes(ert_node, 'payload-field-type')
4810b707
PP
1005
1006 # Calls _expand_ft_aliases() and _apply_fts_inheritance() if the
1007 # trace type node has a `$field-type-aliases` property.
1008 def _expand_fts(self):
1009 # Make sure that the current configuration node is valid
1010 # considering field types are not expanded yet.
2d55dc7d 1011 self._schema_validator.validate(self.config_node,
c3fa1a14 1012 'config/3/config-pre-field-type-expansion')
4810b707
PP
1013
1014 prop_name = '$field-type-aliases'
1015 ft_aliases_node = self._trace_type_node.get(prop_name)
1016
1017 if ft_aliases_node is None:
1018 # If there's no `'$field-type-aliases'` node, then there's
1019 # no field type aliases and therefore no possible
1020 # inheritance.
1021 if prop_name in self._trace_type_node:
1022 del self._trace_type_node[prop_name]
1023
1024 return
1025
1026 # normalize structure field type member nodes
1027 self._normalize_struct_ft_member_nodes()
1028
1029 # first, expand field type aliases
1030 self._expand_ft_aliases()
1031
1032 # next, apply inheritance to create effective field type nodes
1033 self._apply_fts_inheritance()
1034
e8f0d548 1035 # Substitute the event record type node log level aliases with their
4810b707
PP
1036 # numeric equivalents.
1037 #
1038 # Removes the `$log-level-aliases` property of the trace type node.
1039 def _sub_log_level_aliases(self):
1040 # Make sure that the current configuration node is valid
1041 # considering log level aliases are not substituted yet.
2d55dc7d 1042 self._schema_validator.validate(self.config_node,
c3fa1a14 1043 'config/3/config-pre-log-level-alias-sub')
4810b707
PP
1044
1045 log_level_aliases_prop_name = '$log-level-aliases'
1046 log_level_aliases_node = self._trace_type_node.get(log_level_aliases_prop_name)
1047
1048 if log_level_aliases_prop_name in self._trace_type_node:
1049 del self._trace_type_node[log_level_aliases_prop_name]
1050
1051 if log_level_aliases_node is None:
1052 # no log level aliases
1053 return
1054
1055 # substitute log level aliases
e8f0d548 1056 for dst_name, dst_node in self._trace_type_node['data-stream-types'].items():
4810b707 1057 try:
e8f0d548 1058 for ert_name, ert_node in dst_node['event-record-types'].items():
4810b707
PP
1059 try:
1060 prop_name = 'log-level'
e8f0d548 1061 ll_node = ert_node.get(prop_name)
4810b707
PP
1062
1063 if ll_node is None:
1064 continue
1065
1066 if type(ll_node) is str:
1067 if ll_node not in log_level_aliases_node:
1068 raise _ConfigurationParseError(f'`{prop_name}` property',
1069 f'Log level alias `{ll_node}` does not exist')
1070
e8f0d548 1071 ert_node[prop_name] = log_level_aliases_node[ll_node]
4810b707 1072 except _ConfigurationParseError as exc:
e8f0d548 1073 _append_error_ctx(exc, f'Event record type `{ert_name}`')
4810b707 1074 except _ConfigurationParseError as exc:
e8f0d548 1075 _append_error_ctx(exc, f'Data stream type `{dst_name}`')
4810b707
PP
1076
1077 # Generator of parent node and key pairs for all the nodes,
1078 # recursively, of `node`.
1079 #
1080 # It is safe to delete a yielded node during the iteration.
1081 @staticmethod
2d55dc7d 1082 def _props(node: Any) -> Iterable[Tuple[Any, str]]:
4810b707
PP
1083 if type(node) is collections.OrderedDict:
1084 for key in list(node):
1085 yield from _Parser._props(node[key])
1086 yield node, key
1087 elif type(node) is list:
1088 for item_node in node:
1089 yield from _Parser._props(item_node)
1090
2d55dc7d
PP
1091 def _trace_type_props(self) -> Iterable[Tuple[Any, str]]:
1092 yield from _Parser._props(self.config_node['trace']['type'])
4810b707
PP
1093
1094 # Normalize the properties of the configuration node.
1095 #
1096 # This method, for each property of the trace type node:
1097 #
1098 # 1. Removes it if it's `None` (means default).
1099 #
1100 # 2. Chooses a specific `class` property value.
1101 #
313a0bd9 1102 # 3. Chooses a specific `byte-order`/`native-byte-order` property
4810b707
PP
1103 # value.
1104 #
1105 # 4. Chooses a specific `preferred-display-base` property value.
1106 #
1107 # This method also applies 1. to the trace node's `environment`
1108 # property.
1109 def _normalize_props(self):
2d55dc7d 1110 def normalize_byte_order_prop(parent_node: _MapNode, key: str):
4810b707
PP
1111 node = parent_node[key]
1112
1113 if node in ['be', 'big']:
1114 parent_node[key] = 'big-endian'
1115 elif node in ['le', 'little']:
1116 parent_node[key] = 'little-endian'
1117
2d55dc7d 1118 trace_node = self.config_node['trace']
313a0bd9 1119 normalize_byte_order_prop(self._trace_type_node, 'native-byte-order')
4810b707
PP
1120
1121 for parent_node, key in self._trace_type_props():
1122 node = parent_node[key]
1123
1124 if node is None:
1125 # a `None` property is equivalent to not having it
1126 del parent_node[key]
1127 continue
1128
1129 if key == 'class' and type(node) is str:
1130 # field type class aliases
1131 if node in ['uint', 'unsigned-int']:
1132 parent_node[key] = 'unsigned-integer'
1133 elif node in ['sint', 'signed-int']:
1134 parent_node[key] = 'signed-integer'
1135 elif node in ['uenum', 'unsigned-enum']:
1136 parent_node[key] = 'unsigned-enumeration'
1137 elif node in ['senum', 'signed-enum']:
1138 parent_node[key] = 'signed-enumeration'
1139 elif node == 'str':
1140 parent_node[key] = 'string'
1141 elif node == 'struct':
1142 parent_node[key] = 'structure'
4810b707
PP
1143 elif key == 'preferred-display-base' and type(node) is str:
1144 # display base aliases
1145 if node == 'bin':
1146 parent_node[key] = 'binary'
1147 elif node == 'oct':
1148 parent_node[key] = 'octal'
1149 elif node == 'dec':
1150 parent_node[key] = 'decimal'
1151 elif node == 'hex':
1152 parent_node[key] = 'hexadecimal'
1153
1154 prop_name = 'environment'
1155
1156 if prop_name in trace_node:
1157 node = trace_node[prop_name]
1158
1159 if node is None:
1160 del trace_node[prop_name]
1161
313a0bd9
PP
1162 # Sets the parser's native byte order.
1163 def _set_native_byte_order(self):
1164 self._native_byte_order_node = self._trace_type_node['native-byte-order']
1165 self._native_byte_order = self._byte_order_from_node(self._native_byte_order_node)
4810b707 1166
e8f0d548
PP
1167 # Processes the inclusions of the event record type node
1168 # `ert_node`, returning the effective node.
1169 def _process_ert_node_include(self, ert_node: _MapNode) -> _MapNode:
1170 # Make sure the event record type node is valid for the
1171 # inclusion processing stage.
1172 self._schema_validator.validate(ert_node, 'config/3/ert-pre-include')
4810b707
PP
1173
1174 # process inclusions
e8f0d548 1175 return self._process_node_include(ert_node, self._process_ert_node_include)
4810b707 1176
e8f0d548
PP
1177 # Processes the inclusions of the data stream type node `dst_node`,
1178 # returning the effective node.
1179 def _process_dst_node_include(self, dst_node: _MapNode) -> _MapNode:
1180 def process_children_include(dst_node: _MapNode):
1181 prop_name = 'event-record-types'
4810b707 1182
e8f0d548
PP
1183 if prop_name in dst_node:
1184 erts_node = dst_node[prop_name]
4810b707 1185
e8f0d548
PP
1186 for key in list(erts_node):
1187 erts_node[key] = self._process_ert_node_include(erts_node[key])
4810b707 1188
e8f0d548 1189 # Make sure the data stream type node is valid for the inclusion
4810b707 1190 # processing stage.
e8f0d548 1191 self._schema_validator.validate(dst_node, 'config/3/dst-pre-include')
4810b707
PP
1192
1193 # process inclusions
e8f0d548 1194 return self._process_node_include(dst_node, self._process_dst_node_include,
4810b707
PP
1195 process_children_include)
1196
1197 # Processes the inclusions of the clock type node `clk_type_node`,
1198 # returning the effective node.
2d55dc7d 1199 def _process_clk_type_node_include(self, clk_type_node: _MapNode) -> _MapNode:
4810b707
PP
1200 # Make sure the clock type node is valid for the inclusion
1201 # processing stage.
c3fa1a14 1202 self._schema_validator.validate(clk_type_node, 'config/3/clock-type-pre-include')
4810b707
PP
1203
1204 # process inclusions
1205 return self._process_node_include(clk_type_node, self._process_clk_type_node_include)
1206
1207 # Processes the inclusions of the trace type node `trace_type_node`,
1208 # returning the effective node.
2d55dc7d
PP
1209 def _process_trace_type_node_include(self, trace_type_node: _MapNode) -> _MapNode:
1210 def process_children_include(trace_type_node: _MapNode):
4810b707
PP
1211 prop_name = 'clock-types'
1212
1213 if prop_name in trace_type_node:
1214 clk_types_node = trace_type_node[prop_name]
1215
1216 for key in list(clk_types_node):
1217 clk_types_node[key] = self._process_clk_type_node_include(clk_types_node[key])
1218
e8f0d548 1219 prop_name = 'data-stream-types'
4810b707
PP
1220
1221 if prop_name in trace_type_node:
e8f0d548 1222 dsts_node = trace_type_node[prop_name]
4810b707 1223
e8f0d548
PP
1224 for key in list(dsts_node):
1225 dsts_node[key] = self._process_dst_node_include(dsts_node[key])
4810b707
PP
1226
1227 # Make sure the trace type node is valid for the inclusion
1228 # processing stage.
c3fa1a14 1229 self._schema_validator.validate(trace_type_node, 'config/3/trace-type-pre-include')
4810b707
PP
1230
1231 # process inclusions
1232 return self._process_node_include(trace_type_node, self._process_trace_type_node_include,
1233 process_children_include)
1234
1235 # Processes the inclusions of the trace node `trace_node`, returning
1236 # the effective node.
2d55dc7d
PP
1237 def _process_trace_node_include(self, trace_node: _MapNode) -> _MapNode:
1238 def process_children_include(trace_node: _MapNode):
4810b707 1239 prop_name = 'type'
d51033ee
PP
1240
1241 if prop_name in trace_node:
1242 trace_node[prop_name] = self._process_trace_type_node_include(trace_node[prop_name])
4810b707
PP
1243
1244 # Make sure the trace node is valid for the inclusion processing
1245 # stage.
c3fa1a14 1246 self._schema_validator.validate(trace_node, 'config/3/trace-pre-include')
4810b707
PP
1247
1248 # process inclusions
1249 return self._process_node_include(trace_node, self._process_trace_node_include,
1250 process_children_include)
1251
1252 # Processes the inclusions of the configuration node, modifying it
1253 # during the process.
1254 def _process_config_includes(self):
1255 # Process inclusions in this order:
1256 #
e8f0d548
PP
1257 # 1. Clock type node and event record type nodes (the order
1258 # between those is not important).
4810b707 1259 #
e8f0d548 1260 # 2. Data stream type nodes.
4810b707
PP
1261 #
1262 # 3. Trace type node.
1263 #
1264 # 4. Trace node.
1265 #
1266 # This is because:
1267 #
1268 # * A trace node can include a trace type node, clock type
e8f0d548 1269 # nodes, data stream type nodes, and event record type nodes.
4810b707 1270 #
e8f0d548
PP
1271 # * A trace type node can include clock type nodes, data stream
1272 # type nodes, and event record type nodes.
4810b707 1273 #
e8f0d548 1274 # * A data stream type node can include event record type nodes.
4810b707
PP
1275 #
1276 # First, make sure the configuration node itself is valid for
1277 # the inclusion processing stage.
c3fa1a14 1278 self._schema_validator.validate(self.config_node, 'config/3/config-pre-include')
4810b707
PP
1279
1280 # Process trace node inclusions.
1281 #
1282 # self._process_trace_node_include() returns a new (or the same)
1283 # trace node without any `$include` property in it, recursively.
2d55dc7d 1284 self.config_node['trace'] = self._process_trace_node_include(self.config_node['trace'])
4810b707
PP
1285
1286 def _parse(self):
1287 # process configuration node inclusions
1288 self._process_config_includes()
1289
1290 # Expand field type nodes.
1291 #
1292 # This process:
1293 #
1294 # 1. Replaces field type aliases with "effective" field type
1295 # nodes, recursively.
1296 #
1297 # After this step, the `$field-type-aliases` property of the
1298 # trace type node is gone.
1299 #
1300 # 2. Applies inheritance, following the `$inherit` properties.
1301 #
1302 # After this step, field type nodes do not contain `$inherit`
1303 # properties.
1304 #
1305 # This is done blindly, in that the process _doesn't_ validate
1306 # field type nodes at this point.
1307 self._expand_fts()
1308
1309 # Substitute log level aliases.
1310 #
1311 # This process:
1312 #
e8f0d548
PP
1313 # 1. Replaces log level aliases in event record type nodes with
1314 # their numeric equivalents as found in the
1315 # `$log-level-aliases` property of the trace type node.
4810b707
PP
1316 #
1317 # 2. Removes the `$log-level-aliases` property from the trace
1318 # type node.
1319 self._sub_log_level_aliases()
1320
1321 # At this point, the configuration node must be valid as an
1322 # effective configuration node.
c3fa1a14 1323 self._schema_validator.validate(self.config_node, 'config/3/config')
4810b707
PP
1324
1325 # Normalize properties.
1326 #
1327 # This process removes `None` properties and chooses specific
1328 # enumerators when aliases exist (for example, `big-endian`
1329 # instead of `be`).
1330 #
1331 # The goal of this is that, if the user then gets this parser's
1332 # `config_node` property, it has a normal and very readable
1333 # form.
1334 #
1335 # It also makes _create_config() easier to implement because it
1336 # doesn't need to check for `None` nodes or enumerator aliases.
1337 self._normalize_props()
1338
313a0bd9
PP
1339 # Set the native byte order.
1340 self._set_native_byte_order()
4810b707
PP
1341
1342 # Create a barectf configuration object from the configuration
1343 # node.
1344 self._create_config()
1345
1346 @property
2d55dc7d 1347 def config(self) -> barectf_config.Configuration:
4810b707
PP
1348 return self._config
1349
1350 @property
2d55dc7d
PP
1351 def config_node(self) -> _MapNode:
1352 return typing.cast(barectf_config_parse_common._ConfigNodeV3, self._root_node).config_node
This page took 0.108772 seconds and 4 git commands to generate.