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