config: replace trace type's default BO with configuration's target BO
[deliverable/barectf.git] / barectf / config_parse_v3.py
... / ...
CommitLineData
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
27from barectf.config_parse_common import _MapNode
28import barectf.config as barectf_config
29from barectf.config import _OptFt, _OptStructFt
30import collections
31import uuid
32from barectf.typing import Count, Alignment, VersionNumber
33from typing import Optional, List, Dict, Any, TextIO, Set, Iterable, Callable, Tuple, Type
34import typing
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
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]] = {
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
71 def _validate_alignment(alignment: Alignment, ctx_obj_name: str):
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
83 def _validate_iden(iden: str, ctx_obj_name: str, prop: str):
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
108 def _alignment_prop(ft_node: _MapNode, prop_name: str) -> Alignment:
109 alignment = ft_node.get(prop_name)
110
111 if alignment is not None:
112 _Parser._validate_alignment(alignment, '`prop_name` property')
113
114 return Alignment(alignment)
115
116 @property
117 def _trace_type_node(self) -> _MapNode:
118 return self.config_node['trace']['type']
119
120 @staticmethod
121 def _byte_order_from_node(node: str) -> barectf_config.ByteOrder:
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__().
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:
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__().
145 def _create_common_int_ft(self, ft_node: _MapNode,
146 ft_type: Type[barectf_config._IntegerFieldType], *args) -> barectf_config._IntegerFieldType:
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')]
153 return typing.cast(barectf_config._IntegerFieldType,
154 self._create_common_bit_array_ft(ft_node, ft_type, None,
155 preferred_display_base, *args))
156
157 # Creates an integer field type from the unsigned/signed integer
158 # field type node `ft_node`.
159 def _create_int_ft(self, ft_node: _MapNode) -> barectf_config._IntegerFieldType:
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`.
168 def _create_enum_ft(self, ft_node: _MapNode) -> barectf_config._EnumerationFieldType:
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
189 return typing.cast(barectf_config._EnumerationFieldType,
190 self._create_common_int_ft(ft_node, ft_type,
191 barectf_config.EnumerationFieldTypeMappings(mappings)))
192
193 # Creates a real field type from the real field type node `ft_node`.
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)))
198
199 # Creates a string field type from the string field type node
200 # `ft_node`.
201 def _create_string_ft(self, ft_node: _MapNode) -> barectf_config.StringFieldType:
202 return barectf_config.StringFieldType()
203
204 # Creates a static array field type from the static array field type
205 # node `ft_node`.
206 def _create_static_array_ft(self, ft_node: _MapNode) -> barectf_config.StaticArrayFieldType:
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.
221 def _create_struct_ft_members(self, members_node: List[_MapNode], prop_name: str):
222 members = collections.OrderedDict()
223 member_names: Set[str] = set()
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`.
256 def _create_struct_ft(self, ft_node: _MapNode) -> barectf_config.StructureFieldType:
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`.
272 def _create_ft(self, ft_node: _MapNode) -> barectf_config._FieldType:
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.
277 def _try_create_ft(self, parent_node: _MapNode, key: str) -> _OptFt:
278 if key not in parent_node:
279 return None
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
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
296 # Returns the total number of members in the structure field type
297 # node `ft_node` if it exists, otherwise 0.
298 @staticmethod
299 def _total_struct_ft_node_members(ft_node: Optional[_MapNode]) -> Count:
300 if ft_node is None:
301 return Count(0)
302
303 members_node = ft_node.get('members')
304
305 if members_node is None:
306 return Count(0)
307
308 return Count(len(members_node))
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.
318 def _create_ev_type(self, name: str, ev_type_node: _MapNode, ev_member_count: Count) -> barectf_config.EventType:
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'
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)))
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'),
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))
337 except _ConfigurationParseError as exc:
338 _append_error_ctx(exc, f'Event type `{name}`')
339
340 # satisfy static type checker (never reached)
341 raise
342
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.
359 def _feature_ft(self, parent_node: _MapNode, key: str, none: Any = None) -> Any:
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
378 def _create_stream_type(self, name: str, stream_type_node: _MapNode) -> barectf_config.StreamType:
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
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')
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,
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)
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
496 ev_header_common_ctx_member_count = Count(0)
497
498 if ev_features.type_id_field_type is not None:
499 ev_header_common_ctx_member_count = Count(ev_header_common_ctx_member_count + 1)
500
501 if ev_features.time_field_type is not None:
502 ev_header_common_ctx_member_count = Count(ev_header_common_ctx_member_count + 1)
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)
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))
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
513 return barectf_config.StreamType(name, ev_types, def_clk_type, features, None,
514 pkt_ctx_ft_extra_members,
515 self._try_create_struct_ft(stream_type_node,
516 ev_common_ctx_ft_prop_name))
517 except _ConfigurationParseError as exc:
518 _append_error_ctx(exc, f'Stream type `{name}`')
519
520 # satisfy static type checker (never reached)
521 raise
522
523 def _clk_type(self, name: str, prop_name: str) -> barectf_config.ClockType:
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
532 def _create_clk_type(self, name: str, clk_type_node: _MapNode) -> barectf_config.ClockType:
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
541 offset_cycles = Count(0)
542 offset_node = clk_type_node.get('offset')
543
544 if offset_node is not None:
545 offset_seconds = offset_node.get('seconds', 0)
546 offset_cycles = offset_node.get('cycles', Count(0))
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
609 features = barectf_config.TraceTypeFeatures(magic_ft, uuid_ft, stream_type_id_ft,
610 default_byte_order=self._target_byte_order)
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
619 return barectf_config.TraceType(stream_types, trace_type_uuid, features)
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()
626 trace_node = self.config_node['trace']
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
684 opts_node = self.config_node.get('options')
685 iden_prefix = 'barectf_'
686 file_name_prefix = 'barectf'
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
717 self._config = barectf_config.Configuration(trace, self._target_byte_order, opts)
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):
731 def resolve_ft_alias_from(parent_node: _MapNode, key: str):
732 if key not in parent_node:
733 return
734
735 if type(parent_node[key]) not in [collections.OrderedDict, str]:
736 return
737
738 self._resolve_ft_alias_from(ft_aliases_node, parent_node, key)
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):
824 def apply_ft_inheritance(parent_node: _MapNode, key: str):
825 if key not in parent_node:
826 return
827
828 if type(parent_node[key]) is not collections.OrderedDict:
829 return
830
831 self._apply_ft_inheritance(parent_node, key)
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):
886 def normalize_members_node(members_node: List[_MapNode]):
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
899 def normalize_struct_ft_member_nodes(parent_node: _MapNode, key: str):
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
908 ft_node = typing.cast(collections.OrderedDict, ft_node)
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.
965 self._schema_validator.validate(self.config_node,
966 'config/3/config-pre-field-type-expansion')
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.
996 self._schema_validator.validate(self.config_node,
997 'config/3/config-pre-log-level-alias-sub')
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
1036 def _props(node: Any) -> Iterable[Tuple[Any, str]]:
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
1045 def _trace_type_props(self) -> Iterable[Tuple[Any, str]]:
1046 yield from _Parser._props(self.config_node['trace']['type'])
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 #
1056 # 3. Chooses a specific `byte-order`/`target-byte-order` property
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):
1064 def normalize_byte_order_prop(parent_node: _MapNode, key: str):
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
1072 trace_node = self.config_node['trace']
1073 trace_type_node = trace_node['type']
1074 normalize_byte_order_prop(self.config_node, 'target-byte-order')
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
1120 # Substitutes missing/`None` `byte-order` properties with the
1121 # configuration node's target byte order (`target-byte-order`
1122 # property).
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
1132 def set_ft_node_byte_order_prop(parent_node: _MapNode, key: str):
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:
1146 ft_node[prop_name] = self._target_byte_order_node
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
1155 def set_struct_ft_node_members_byte_order_prop(members_node: List[_MapNode]):
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
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)
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.
1221 def _process_ev_type_node_include(self, ev_type_node: _MapNode) -> _MapNode:
1222 # Make sure the event type node is valid for the inclusion
1223 # processing stage.
1224 self._schema_validator.validate(ev_type_node, 'config/3/event-type-pre-include')
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.
1231 def _process_stream_type_node_include(self, stream_type_node: _MapNode) -> _MapNode:
1232 def process_children_include(stream_type_node: _MapNode):
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.
1243 self._schema_validator.validate(stream_type_node, 'config/3/stream-type-pre-include')
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.
1251 def _process_clk_type_node_include(self, clk_type_node: _MapNode) -> _MapNode:
1252 # Make sure the clock type node is valid for the inclusion
1253 # processing stage.
1254 self._schema_validator.validate(clk_type_node, 'config/3/clock-type-pre-include')
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.
1261 def _process_trace_type_node_include(self, trace_type_node: _MapNode) -> _MapNode:
1262 def process_children_include(trace_type_node: _MapNode):
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.
1281 self._schema_validator.validate(trace_type_node, 'config/3/trace-type-pre-include')
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.
1289 def _process_trace_node_include(self, trace_node: _MapNode) -> _MapNode:
1290 def process_children_include(trace_node: _MapNode):
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.
1296 self._schema_validator.validate(trace_node, 'config/3/trace-pre-include')
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.
1328 self._schema_validator.validate(self.config_node, 'config/3/config-pre-include')
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.
1334 self.config_node['trace'] = self._process_trace_node_include(self.config_node['trace'])
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.
1373 self._schema_validator.validate(self.config_node, 'config/3/config')
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.
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
1398 def config(self) -> barectf_config.Configuration:
1399 return self._config
1400
1401 @property
1402 def config_node(self) -> _MapNode:
1403 return typing.cast(barectf_config_parse_common._ConfigNodeV3, self._root_node).config_node
This page took 0.027897 seconds and 4 git commands to generate.