Add Python type hints
[barectf.git] / barectf / config_parse_v3.py
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
24 import barectf.config_parse_common as barectf_config_parse_common
25 from barectf.config_parse_common import _ConfigurationParseError
26 from barectf.config_parse_common import _append_error_ctx
27 from barectf.config_parse_common import _MapNode
28 import barectf.config as barectf_config
29 from barectf.config import _OptFt, _OptStructFt
30 import collections
31 import uuid
32 from barectf.typing import Count, Alignment, VersionNumber
33 from typing import Optional, List, Dict, Any, TextIO, Set, Iterable, Callable, Tuple, Type
34 import 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.
46 class _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 +
326 self._total_struct_ft_node_members(ev_type_node.get(spec_ctx_ft_prop_name)))
327 ev_member_count = Count(ev_member_count +
328 self._total_struct_ft_node_members(ev_type_node.get(payload_ft_prop_name)))
329
330 if ev_member_count == 0:
331 raise _ConfigurationParseError('Event type', 'Event type is empty (no members).')
332
333 # create event type
334 return barectf_config.EventType(name, ev_type_node.get('log-level'),
335 self._try_create_struct_ft(ev_type_node,
336 spec_ctx_ft_prop_name),
337 self._try_create_struct_ft(ev_type_node,
338 payload_ft_prop_name))
339 except _ConfigurationParseError as exc:
340 _append_error_ctx(exc, f'Event type `{name}`')
341
342 # satisfy static type checker (never reached)
343 raise
344
345 # Returns the effective feature field type for the field type
346 # node `parent_node[key]`, if any.
347 #
348 # Returns:
349 #
350 # If `parent_node[key]` is `False`:
351 # `None`.
352 #
353 # If `parent_node[key]` is `True`:
354 # `barectf_config.DEFAULT_FIELD_TYPE`.
355 #
356 # If `parent_node[key]` doesn't exist:
357 # `none` (parameter).
358 #
359 # Otherwise:
360 # A created field type.
361 def _feature_ft(self, parent_node: _MapNode, key: str, none: Any = None) -> Any:
362 if key not in parent_node:
363 # missing: default feature field type
364 return none
365
366 ft_node = parent_node[key]
367 assert ft_node is not None
368
369 if ft_node is True:
370 # default feature field type
371 return barectf_config.DEFAULT_FIELD_TYPE
372
373 if ft_node is False:
374 # disabled feature
375 return None
376
377 assert type(ft_node) is collections.OrderedDict
378 return self._create_ft(ft_node)
379
380 def _create_stream_type(self, name: str, stream_type_node: _MapNode) -> barectf_config.StreamType:
381 try:
382 # validate stream type's name
383 self._validate_iden(name, '`name` property', 'stream type name')
384
385 # get default clock type, if any
386 def_clk_type = None
387 prop_name = '$default-clock-type-name'
388 def_clk_type_name = stream_type_node.get(prop_name)
389
390 if def_clk_type_name is not None:
391 try:
392 def_clk_type = self._clk_type(def_clk_type_name, prop_name)
393 except _ConfigurationParseError as exc:
394 _append_error_ctx(exc, f'`{prop_name}` property')
395
396 # create feature field types
397 pkt_total_size_ft = barectf_config.DEFAULT_FIELD_TYPE
398 pkt_content_size_ft = barectf_config.DEFAULT_FIELD_TYPE
399 pkt_beginning_time_ft = None
400 pkt_end_time_ft = None
401 pkt_discarded_events_counter_ft = None
402 ev_type_id_ft = barectf_config.DEFAULT_FIELD_TYPE
403 ev_time_ft = None
404
405 if def_clk_type is not None:
406 # The stream type has a default clock type. Initialize
407 # the packet beginning time, packet end time, and event
408 # time field types to default field types.
409 #
410 # This means your stream type node only needs a default
411 # clock type name to enable those features
412 # automatically. Those features do not add any parameter
413 # to the tracing event functions.
414 pkt_beginning_time_ft = barectf_config.DEFAULT_FIELD_TYPE
415 pkt_end_time_ft = barectf_config.DEFAULT_FIELD_TYPE
416 ev_time_ft = barectf_config.DEFAULT_FIELD_TYPE
417
418 features_node = stream_type_node.get('$features')
419
420 if features_node is not None:
421 # create packet feature field types
422 pkt_node = features_node.get('packet')
423
424 if pkt_node is not None:
425 pkt_total_size_ft = self._feature_ft(pkt_node, 'total-size-field-type',
426 pkt_total_size_ft)
427 pkt_content_size_ft = self._feature_ft(pkt_node, 'content-size-field-type',
428 pkt_content_size_ft)
429 pkt_beginning_time_ft = self._feature_ft(pkt_node, 'beginning-time-field-type',
430 pkt_beginning_time_ft)
431 pkt_end_time_ft = self._feature_ft(pkt_node, 'end-time-field-type',
432 pkt_end_time_ft)
433 pkt_discarded_events_counter_ft = self._feature_ft(pkt_node,
434 'discarded-events-counter-field-type',
435 pkt_discarded_events_counter_ft)
436
437 # create event feature field types
438 ev_node = features_node.get('event')
439 type_id_ft_prop_name = 'type-id-field-type'
440
441 if ev_node is not None:
442 ev_type_id_ft = self._feature_ft(ev_node, type_id_ft_prop_name, ev_type_id_ft)
443 ev_time_ft = self._feature_ft(ev_node, 'time-field-type', ev_time_ft)
444
445 ev_types_prop_name = 'event-types'
446 ev_type_count = len(stream_type_node[ev_types_prop_name])
447
448 try:
449 if ev_type_id_ft is None and ev_type_count > 1:
450 raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property',
451 'Event type ID field type feature is required because stream type has more than one event type')
452
453 if isinstance(ev_type_id_ft, barectf_config._IntegerFieldType):
454 ev_type_id_int_ft = typing.cast(barectf_config._IntegerFieldType, ev_type_id_ft)
455
456 if ev_type_count > (1 << ev_type_id_int_ft.size):
457 raise _ConfigurationParseError(f'`{type_id_ft_prop_name}` property',
458 f'Field type\'s size ({ev_type_id_int_ft.size} bits) is too small to accomodate {ev_type_count} event types')
459 except _ConfigurationParseError as exc:
460 exc._append_ctx('`event` property')
461 _append_error_ctx(exc, '`$features` property')
462
463 pkt_features = barectf_config.StreamTypePacketFeatures(pkt_total_size_ft,
464 pkt_content_size_ft,
465 pkt_beginning_time_ft,
466 pkt_end_time_ft,
467 pkt_discarded_events_counter_ft)
468 ev_features = barectf_config.StreamTypeEventFeatures(ev_type_id_ft, ev_time_ft)
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 +
507 self._total_struct_ft_node_members(ev_common_ctx_ft_node))
508 ev_types = set()
509
510 for ev_name, ev_type_node in stream_type_node[ev_types_prop_name].items():
511 ev_types.add(self._create_ev_type(ev_name, ev_type_node, ev_header_common_ctx_member_count))
512
513 # create stream type
514 return barectf_config.StreamType(name, ev_types, def_clk_type, features,
515 pkt_ctx_ft_extra_members,
516 self._try_create_struct_ft(stream_type_node,
517 ev_common_ctx_ft_prop_name))
518 except _ConfigurationParseError as exc:
519 _append_error_ctx(exc, f'Stream type `{name}`')
520
521 # satisfy static type checker (never reached)
522 raise
523
524 def _clk_type(self, name: str, prop_name: str) -> barectf_config.ClockType:
525 clk_type = self._clk_types.get(name)
526
527 if clk_type is None:
528 raise _ConfigurationParseError(f'`{prop_name}` property',
529 f'Clock type `{name}` does not exist')
530
531 return clk_type
532
533 def _create_clk_type(self, name: str, clk_type_node: _MapNode) -> barectf_config.ClockType:
534 self._validate_iden(name, '`name` property', 'clock type name')
535 clk_type_uuid = None
536 uuid_node = clk_type_node.get('uuid')
537
538 if uuid_node is not None:
539 clk_type_uuid = uuid.UUID(uuid_node)
540
541 offset_seconds = 0
542 offset_cycles = Count(0)
543 offset_node = clk_type_node.get('offset')
544
545 if offset_node is not None:
546 offset_seconds = offset_node.get('seconds', 0)
547 offset_cycles = offset_node.get('cycles', Count(0))
548
549 return barectf_config.ClockType(name, clk_type_node.get('frequency', int(1e9)),
550 clk_type_uuid, clk_type_node.get('description'),
551 clk_type_node.get('precision', 0),
552 barectf_config.ClockTypeOffset(offset_seconds, offset_cycles),
553 clk_type_node.get('origin-is-unix-epoch', False))
554
555 def _create_clk_types(self):
556 self._clk_types = {}
557
558 for clk_type_name, clk_type_node in self._trace_type_node.get('clock-types', {}).items():
559 self._clk_types[clk_type_name] = self._create_clk_type(clk_type_name, clk_type_node)
560
561 def _create_trace_type(self):
562 try:
563 # create clock types (_create_stream_type() needs them)
564 self._create_clk_types()
565
566 # get UUID
567 trace_type_uuid = None
568 uuid_node = self._trace_type_node.get('uuid')
569
570 if uuid_node is not None:
571 if uuid_node == 'auto':
572 trace_type_uuid = uuid.uuid1()
573 else:
574 trace_type_uuid = uuid.UUID(uuid_node)
575
576 # create feature field types
577 magic_ft = barectf_config.DEFAULT_FIELD_TYPE
578 uuid_ft = None
579 stream_type_id_ft = barectf_config.DEFAULT_FIELD_TYPE
580
581 if trace_type_uuid is not None:
582 # Trace type has a UUID: initialize UUID field type to
583 # a default field type.
584 uuid_ft = barectf_config.DEFAULT_FIELD_TYPE
585
586 features_node = self._trace_type_node.get('$features')
587 stream_type_id_ft_prop_name = 'stream-type-id-field-type'
588
589 if features_node is not None:
590 magic_ft = self._feature_ft(features_node, 'magic-field-type',
591 magic_ft)
592 uuid_ft = self._feature_ft(features_node, 'uuid-field-type', uuid_ft)
593 stream_type_id_ft = self._feature_ft(features_node, stream_type_id_ft_prop_name,
594 stream_type_id_ft)
595
596 stream_types_prop_name = 'stream-types'
597 stream_type_count = len(self._trace_type_node[stream_types_prop_name])
598
599 try:
600 if stream_type_id_ft is None and stream_type_count > 1:
601 raise _ConfigurationParseError(f'`{stream_type_id_ft_prop_name}` property',
602 'Stream type ID field type feature is required because trace type has more than one stream type')
603
604 if isinstance(stream_type_id_ft, barectf_config._FieldType) and stream_type_count > (1 << stream_type_id_ft.size):
605 raise _ConfigurationParseError(f'`{stream_type_id_ft_prop_name}` property',
606 f'Field type\'s size ({stream_type_id_ft.size} bits) is too small to accomodate {stream_type_count} stream types')
607 except _ConfigurationParseError as exc:
608 _append_error_ctx(exc, '`$features` property')
609
610 features = barectf_config.TraceTypeFeatures(magic_ft, uuid_ft, stream_type_id_ft)
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, self._default_byte_order,
620 trace_type_uuid, features)
621 except _ConfigurationParseError as exc:
622 _append_error_ctx(exc, 'Trace type')
623
624 def _create_trace(self):
625 try:
626 trace_type = self._create_trace_type()
627 trace_node = self.config_node['trace']
628 env = None
629 env_node = trace_node.get('environment')
630
631 if env_node is not None:
632 # validate each environment variable name
633 for name in env_node:
634 self._validate_iden(name, '`environment` property',
635 'environment variable name')
636
637 # the node already has the expected structure
638 env = barectf_config.TraceEnvironment(env_node)
639
640 return barectf_config.Trace(trace_type, env)
641
642 except _ConfigurationParseError as exc:
643 _append_error_ctx(exc, 'Trace')
644
645 def _create_config(self):
646 # create trace first
647 trace = self._create_trace()
648
649 # find default stream type, if any
650 def_stream_type = None
651
652 for stream_type_name, stream_type_node in self._trace_type_node['stream-types'].items():
653 prop_name = '$is-default'
654 is_default = stream_type_node.get(prop_name)
655
656 if is_default is True:
657 if def_stream_type is not None:
658 exc = _ConfigurationParseError(f'`{prop_name}` property',
659 f'Duplicate default stream type (`{def_stream_type.name}`)')
660 exc._append_ctx(f'Stream type `{stream_type_name}`')
661 _append_error_ctx(exc, 'Trace type')
662
663 def_stream_type = trace.type.stream_type(stream_type_name)
664
665 # create clock type C type mapping
666 clk_types_node = self._trace_type_node.get('clock-types')
667 clk_type_c_types = None
668
669 if clk_types_node is not None:
670 clk_type_c_types = collections.OrderedDict()
671
672 for stream_type in trace.type.stream_types:
673 if stream_type.default_clock_type is None:
674 continue
675
676 clk_type_node = clk_types_node[stream_type.default_clock_type.name]
677 c_type = clk_type_node.get('$c-type')
678
679 if c_type is not None:
680 clk_type_c_types[stream_type.default_clock_type] = c_type
681
682 # create options
683 iden_prefix_def = False
684 def_stream_type_name_def = False
685 opts_node = self.config_node.get('options')
686 iden_prefix = 'barectf_'
687 file_name_prefix = 'barectf'
688
689 if opts_node is not None:
690 code_gen_opts_node = opts_node.get('code-generation')
691
692 if code_gen_opts_node is not None:
693 prefix_node = code_gen_opts_node.get('prefix', 'barectf')
694
695 if type(prefix_node) is str:
696 # automatic prefixes
697 iden_prefix = f'{prefix_node}_'
698 file_name_prefix = prefix_node
699 else:
700 iden_prefix = prefix_node['identifier']
701 file_name_prefix = prefix_node['file-name']
702
703 header_opts = code_gen_opts_node.get('header')
704
705 if header_opts is not None:
706 iden_prefix_def = header_opts.get('identifier-prefix-definition', False)
707 def_stream_type_name_def = header_opts.get('default-stream-type-name-definition',
708 False)
709
710 header_opts = barectf_config.ConfigurationCodeGenerationHeaderOptions(iden_prefix_def,
711 def_stream_type_name_def)
712 cg_opts = barectf_config.ConfigurationCodeGenerationOptions(iden_prefix, file_name_prefix,
713 def_stream_type, header_opts,
714 clk_type_c_types)
715 opts = barectf_config.ConfigurationOptions(cg_opts)
716
717 # create configuration
718 self._config = barectf_config.Configuration(trace, opts)
719
720 # Expands the field type aliases found in the trace type node.
721 #
722 # This method modifies the trace type node.
723 #
724 # When this method returns:
725 #
726 # * Any field type alias is replaced with its full field type
727 # node equivalent.
728 #
729 # * The `$field-type-aliases` property of the trace type node is
730 # removed.
731 def _expand_ft_aliases(self):
732 def resolve_ft_alias_from(parent_node: _MapNode, key: str):
733 if key not in parent_node:
734 return
735
736 if type(parent_node[key]) not in [collections.OrderedDict, str]:
737 return
738
739 self._resolve_ft_alias_from(ft_aliases_node, parent_node, key)
740
741 ft_aliases_node = self._trace_type_node['$field-type-aliases']
742
743 # Expand field type aliases within trace, stream, and event type
744 # nodes.
745 features_prop_name = '$features'
746
747 try:
748 features_node = self._trace_type_node.get(features_prop_name)
749
750 if features_node is not None:
751 try:
752 resolve_ft_alias_from(features_node, 'magic-field-type')
753 resolve_ft_alias_from(features_node, 'uuid-field-type')
754 resolve_ft_alias_from(features_node, 'stream-type-id-field-type')
755 except _ConfigurationParseError as exc:
756 _append_error_ctx(exc, f'`{features_prop_name}` property')
757 except _ConfigurationParseError as exc:
758 _append_error_ctx(exc, 'Trace type')
759
760 for stream_type_name, stream_type_node in self._trace_type_node['stream-types'].items():
761 try:
762 features_node = stream_type_node.get(features_prop_name)
763
764 if features_node is not None:
765 try:
766 pkt_prop_name = 'packet'
767 pkt_node = features_node.get(pkt_prop_name)
768
769 if pkt_node is not None:
770 try:
771 resolve_ft_alias_from(pkt_node, 'total-size-field-type')
772 resolve_ft_alias_from(pkt_node, 'content-size-field-type')
773 resolve_ft_alias_from(pkt_node, 'beginning-time-field-type')
774 resolve_ft_alias_from(pkt_node, 'end-time-field-type')
775 resolve_ft_alias_from(pkt_node,
776 'discarded-events-counter-field-type')
777 except _ConfigurationParseError as exc:
778 _append_error_ctx(exc, f'`{pkt_prop_name}` property')
779
780 ev_prop_name = 'event'
781 ev_node = features_node.get(ev_prop_name)
782
783 if ev_node is not None:
784 try:
785 resolve_ft_alias_from(ev_node, 'type-id-field-type')
786 resolve_ft_alias_from(ev_node, 'time-field-type')
787 except _ConfigurationParseError as exc:
788 _append_error_ctx(exc, f'`{ev_prop_name}` property')
789 except _ConfigurationParseError as exc:
790 _append_error_ctx(exc, f'`{features_prop_name}` property')
791
792 pkt_ctx_ft_extra_members_prop_name = 'packet-context-field-type-extra-members'
793 pkt_ctx_ft_extra_members_node = stream_type_node.get(pkt_ctx_ft_extra_members_prop_name)
794
795 if pkt_ctx_ft_extra_members_node is not None:
796 try:
797 for member_node in pkt_ctx_ft_extra_members_node:
798 member_node = list(member_node.values())[0]
799 resolve_ft_alias_from(member_node, 'field-type')
800 except _ConfigurationParseError as exc:
801 _append_error_ctx(exc, f'`{pkt_ctx_ft_extra_members_prop_name}` property')
802
803 resolve_ft_alias_from(stream_type_node, 'event-common-context-field-type')
804
805 for ev_type_name, ev_type_node in stream_type_node['event-types'].items():
806 try:
807 resolve_ft_alias_from(ev_type_node, 'specific-context-field-type')
808 resolve_ft_alias_from(ev_type_node, 'payload-field-type')
809 except _ConfigurationParseError as exc:
810 _append_error_ctx(exc, f'Event type `{ev_type_name}`')
811 except _ConfigurationParseError as exc:
812 _append_error_ctx(exc, f'Stream type `{stream_type_name}`')
813
814 # remove the (now unneeded) `$field-type-aliases` property
815 del self._trace_type_node['$field-type-aliases']
816
817 # Applies field type inheritance to all field type nodes found in
818 # the trace type node.
819 #
820 # This method modifies the trace type node.
821 #
822 # When this method returns, no field type node has an `$inherit`
823 # property.
824 def _apply_fts_inheritance(self):
825 def apply_ft_inheritance(parent_node: _MapNode, key: str):
826 if key not in parent_node:
827 return
828
829 if type(parent_node[key]) is not collections.OrderedDict:
830 return
831
832 self._apply_ft_inheritance(parent_node, key)
833
834 features_prop_name = '$features'
835 features_node = self._trace_type_node.get(features_prop_name)
836
837 if features_node is not None:
838 apply_ft_inheritance(features_node, 'magic-field-type')
839 apply_ft_inheritance(features_node, 'uuid-field-type')
840 apply_ft_inheritance(features_node, 'stream-type-id-field-type')
841
842 for stream_type_node in self._trace_type_node['stream-types'].values():
843 features_node = stream_type_node.get(features_prop_name)
844
845 if features_node is not None:
846 pkt_node = features_node.get('packet')
847
848 if pkt_node is not None:
849 apply_ft_inheritance(pkt_node, 'total-size-field-type')
850 apply_ft_inheritance(pkt_node, 'content-size-field-type')
851 apply_ft_inheritance(pkt_node, 'beginning-time-field-type')
852 apply_ft_inheritance(pkt_node, 'end-time-field-type')
853 apply_ft_inheritance(pkt_node, 'discarded-events-counter-field-type')
854
855 ev_node = features_node.get('event')
856
857 if ev_node is not None:
858 apply_ft_inheritance(ev_node, 'type-id-field-type')
859 apply_ft_inheritance(ev_node, 'time-field-type')
860
861 pkt_ctx_ft_extra_members_node = stream_type_node.get('packet-context-field-type-extra-members')
862
863 if pkt_ctx_ft_extra_members_node is not None:
864 for member_node in pkt_ctx_ft_extra_members_node:
865 member_node = list(member_node.values())[0]
866 apply_ft_inheritance(member_node, 'field-type')
867
868 apply_ft_inheritance(stream_type_node, 'event-common-context-field-type')
869
870 for ev_type_node in stream_type_node['event-types'].values():
871 apply_ft_inheritance(ev_type_node, 'specific-context-field-type')
872 apply_ft_inheritance(ev_type_node, 'payload-field-type')
873
874 # Normalizes structure field type member nodes.
875 #
876 # A structure field type member node can look like this:
877 #
878 # - msg: custom-string
879 #
880 # which is the equivalent of this:
881 #
882 # - msg:
883 # field-type: custom-string
884 #
885 # This method normalizes form 1 to use form 2.
886 def _normalize_struct_ft_member_nodes(self):
887 def normalize_members_node(members_node: List[_MapNode]):
888 ft_prop_name = 'field-type'
889
890 for member_node in members_node:
891 member_name, val_node = list(member_node.items())[0]
892
893 if type(val_node) is str:
894 member_node[member_name] = collections.OrderedDict({
895 ft_prop_name: val_node
896 })
897
898 normalize_struct_ft_member_nodes(member_node[member_name], ft_prop_name)
899
900 def normalize_struct_ft_member_nodes(parent_node: _MapNode, key: str):
901 if type(parent_node) is not collections.OrderedDict:
902 return
903
904 ft_node = parent_node.get(key)
905
906 if type(ft_node) is not collections.OrderedDict:
907 return
908
909 ft_node = typing.cast(collections.OrderedDict, ft_node)
910 members_nodes = ft_node.get('members')
911
912 if members_nodes is not None:
913 normalize_members_node(members_nodes)
914
915 prop_name = '$field-type-aliases'
916 ft_aliases_node = self._trace_type_node.get(prop_name)
917
918 if ft_aliases_node is not None:
919 for alias in ft_aliases_node:
920 normalize_struct_ft_member_nodes(ft_aliases_node, alias)
921
922 features_prop_name = '$features'
923 features_node = self._trace_type_node.get(features_prop_name)
924
925 if features_node is not None:
926 normalize_struct_ft_member_nodes(features_node, 'magic-field-type')
927 normalize_struct_ft_member_nodes(features_node, 'uuid-field-type')
928 normalize_struct_ft_member_nodes(features_node, 'stream-type-id-field-type')
929
930 for stream_type_node in self._trace_type_node['stream-types'].values():
931 features_node = stream_type_node.get(features_prop_name)
932
933 if features_node is not None:
934 pkt_node = features_node.get('packet')
935
936 if pkt_node is not None:
937 normalize_struct_ft_member_nodes(pkt_node, 'total-size-field-type')
938 normalize_struct_ft_member_nodes(pkt_node, 'content-size-field-type')
939 normalize_struct_ft_member_nodes(pkt_node, 'beginning-time-field-type')
940 normalize_struct_ft_member_nodes(pkt_node, 'end-time-field-type')
941 normalize_struct_ft_member_nodes(pkt_node,
942 'discarded-events-counter-field-type')
943
944 ev_node = features_node.get('event')
945
946 if ev_node is not None:
947 normalize_struct_ft_member_nodes(ev_node, 'type-id-field-type')
948 normalize_struct_ft_member_nodes(ev_node, 'time-field-type')
949
950 pkt_ctx_ft_extra_members_node = stream_type_node.get('packet-context-field-type-extra-members')
951
952 if pkt_ctx_ft_extra_members_node is not None:
953 normalize_members_node(pkt_ctx_ft_extra_members_node)
954
955 normalize_struct_ft_member_nodes(stream_type_node, 'event-common-context-field-type')
956
957 for ev_type_node in stream_type_node['event-types'].values():
958 normalize_struct_ft_member_nodes(ev_type_node, 'specific-context-field-type')
959 normalize_struct_ft_member_nodes(ev_type_node, 'payload-field-type')
960
961 # Calls _expand_ft_aliases() and _apply_fts_inheritance() if the
962 # trace type node has a `$field-type-aliases` property.
963 def _expand_fts(self):
964 # Make sure that the current configuration node is valid
965 # considering field types are not expanded yet.
966 self._schema_validator.validate(self.config_node,
967 '3/config/config-pre-field-type-expansion')
968
969 prop_name = '$field-type-aliases'
970 ft_aliases_node = self._trace_type_node.get(prop_name)
971
972 if ft_aliases_node is None:
973 # If there's no `'$field-type-aliases'` node, then there's
974 # no field type aliases and therefore no possible
975 # inheritance.
976 if prop_name in self._trace_type_node:
977 del self._trace_type_node[prop_name]
978
979 return
980
981 # normalize structure field type member nodes
982 self._normalize_struct_ft_member_nodes()
983
984 # first, expand field type aliases
985 self._expand_ft_aliases()
986
987 # next, apply inheritance to create effective field type nodes
988 self._apply_fts_inheritance()
989
990 # Substitute the event type node log level aliases with their
991 # numeric equivalents.
992 #
993 # Removes the `$log-level-aliases` property of the trace type node.
994 def _sub_log_level_aliases(self):
995 # Make sure that the current configuration node is valid
996 # considering log level aliases are not substituted yet.
997 self._schema_validator.validate(self.config_node,
998 '3/config/config-pre-log-level-alias-sub')
999
1000 log_level_aliases_prop_name = '$log-level-aliases'
1001 log_level_aliases_node = self._trace_type_node.get(log_level_aliases_prop_name)
1002
1003 if log_level_aliases_prop_name in self._trace_type_node:
1004 del self._trace_type_node[log_level_aliases_prop_name]
1005
1006 if log_level_aliases_node is None:
1007 # no log level aliases
1008 return
1009
1010 # substitute log level aliases
1011 for stream_type_name, stream_type_node in self._trace_type_node['stream-types'].items():
1012 try:
1013 for ev_type_name, ev_type_node in stream_type_node['event-types'].items():
1014 try:
1015 prop_name = 'log-level'
1016 ll_node = ev_type_node.get(prop_name)
1017
1018 if ll_node is None:
1019 continue
1020
1021 if type(ll_node) is str:
1022 if ll_node not in log_level_aliases_node:
1023 raise _ConfigurationParseError(f'`{prop_name}` property',
1024 f'Log level alias `{ll_node}` does not exist')
1025
1026 ev_type_node[prop_name] = log_level_aliases_node[ll_node]
1027 except _ConfigurationParseError as exc:
1028 _append_error_ctx(exc, f'Event type `{ev_type_name}`')
1029 except _ConfigurationParseError as exc:
1030 _append_error_ctx(exc, f'Stream type `{stream_type_name}`')
1031
1032 # Generator of parent node and key pairs for all the nodes,
1033 # recursively, of `node`.
1034 #
1035 # It is safe to delete a yielded node during the iteration.
1036 @staticmethod
1037 def _props(node: Any) -> Iterable[Tuple[Any, str]]:
1038 if type(node) is collections.OrderedDict:
1039 for key in list(node):
1040 yield from _Parser._props(node[key])
1041 yield node, key
1042 elif type(node) is list:
1043 for item_node in node:
1044 yield from _Parser._props(item_node)
1045
1046 def _trace_type_props(self) -> Iterable[Tuple[Any, str]]:
1047 yield from _Parser._props(self.config_node['trace']['type'])
1048
1049 # Normalize the properties of the configuration node.
1050 #
1051 # This method, for each property of the trace type node:
1052 #
1053 # 1. Removes it if it's `None` (means default).
1054 #
1055 # 2. Chooses a specific `class` property value.
1056 #
1057 # 3. Chooses a specific `byte-order`/`$default-byte-order` property
1058 # value.
1059 #
1060 # 4. Chooses a specific `preferred-display-base` property value.
1061 #
1062 # This method also applies 1. to the trace node's `environment`
1063 # property.
1064 def _normalize_props(self):
1065 def normalize_byte_order_prop(parent_node: _MapNode, key: str):
1066 node = parent_node[key]
1067
1068 if node in ['be', 'big']:
1069 parent_node[key] = 'big-endian'
1070 elif node in ['le', 'little']:
1071 parent_node[key] = 'little-endian'
1072
1073 trace_node = self.config_node['trace']
1074 trace_type_node = trace_node['type']
1075 prop_name = '$default-byte-order'
1076
1077 if prop_name in trace_type_node and type(trace_type_node[prop_name]) is str:
1078 normalize_byte_order_prop(trace_type_node, prop_name)
1079
1080 for parent_node, key in self._trace_type_props():
1081 node = parent_node[key]
1082
1083 if node is None:
1084 # a `None` property is equivalent to not having it
1085 del parent_node[key]
1086 continue
1087
1088 if key == 'class' and type(node) is str:
1089 # field type class aliases
1090 if node in ['uint', 'unsigned-int']:
1091 parent_node[key] = 'unsigned-integer'
1092 elif node in ['sint', 'signed-int']:
1093 parent_node[key] = 'signed-integer'
1094 elif node in ['uenum', 'unsigned-enum']:
1095 parent_node[key] = 'unsigned-enumeration'
1096 elif node in ['senum', 'signed-enum']:
1097 parent_node[key] = 'signed-enumeration'
1098 elif node == 'str':
1099 parent_node[key] = 'string'
1100 elif node == 'struct':
1101 parent_node[key] = 'structure'
1102 elif key == 'byte-order' and type(node) is str:
1103 # byte order aliases
1104 normalize_byte_order_prop(parent_node, key)
1105 elif key == 'preferred-display-base' and type(node) is str:
1106 # display base aliases
1107 if node == 'bin':
1108 parent_node[key] = 'binary'
1109 elif node == 'oct':
1110 parent_node[key] = 'octal'
1111 elif node == 'dec':
1112 parent_node[key] = 'decimal'
1113 elif node == 'hex':
1114 parent_node[key] = 'hexadecimal'
1115
1116 prop_name = 'environment'
1117
1118 if prop_name in trace_node:
1119 node = trace_node[prop_name]
1120
1121 if node is None:
1122 del trace_node[prop_name]
1123
1124 # Substitutes missing/`None` `byte-order` properties with the trace
1125 # type node's default byte order (`$default-byte-order` property),
1126 # if any.
1127 def _sub_ft_nodes_byte_order(self):
1128 ba_ft_class_names = {
1129 'unsigned-integer',
1130 'signed-integer',
1131 'unsigned-enumeration',
1132 'signed-enumeration',
1133 'real',
1134 }
1135
1136 def set_ft_node_byte_order_prop(parent_node: _MapNode, key: str):
1137 if key not in parent_node:
1138 return
1139
1140 ft_node = parent_node[key]
1141
1142 if type(ft_node) is not collections.OrderedDict:
1143 return
1144
1145 if ft_node['class'] in ba_ft_class_names:
1146 prop_name = 'byte-order'
1147 byte_order_node = ft_node.get(prop_name)
1148
1149 if byte_order_node is None:
1150 if default_byte_order_node is None:
1151 raise _ConfigurationParseError(f'`{key}` property`',
1152 '`byte-order` property is not set or null, but trace type has no `$default-byte-order` property')
1153
1154 ft_node[prop_name] = default_byte_order_node
1155
1156 members_node = ft_node.get('members')
1157
1158 if members_node is not None:
1159 set_struct_ft_node_members_byte_order_prop(members_node)
1160
1161 set_ft_node_byte_order_prop(ft_node, 'element-field-type')
1162
1163 def set_struct_ft_node_members_byte_order_prop(members_node: List[_MapNode]):
1164 for member_node in members_node:
1165 member_name, member_node = list(member_node.items())[0]
1166
1167 try:
1168 set_ft_node_byte_order_prop(member_node, 'field-type')
1169 except _ConfigurationParseError as exc:
1170 _append_error_ctx(exc, f'Structure field type member `{member_name}`')
1171
1172 default_byte_order_node = self._trace_type_node.get('$default-byte-order')
1173
1174 self._default_byte_order = None
1175
1176 if default_byte_order_node is not None:
1177 self._default_byte_order = self._byte_order_from_node(default_byte_order_node)
1178
1179 features_prop_name = '$features'
1180 features_node = self._trace_type_node.get(features_prop_name)
1181
1182 if features_node is not None:
1183 try:
1184 set_ft_node_byte_order_prop(features_node, 'magic-field-type')
1185 set_ft_node_byte_order_prop(features_node, 'uuid-field-type')
1186 set_ft_node_byte_order_prop(features_node, 'stream-type-id-field-type')
1187 except _ConfigurationParseError as exc:
1188 exc._append_ctx(exc, f'`{features_prop_name}` property')
1189 _append_error_ctx(exc, 'Trace type')
1190
1191 for stream_type_name, stream_type_node in self._trace_type_node['stream-types'].items():
1192 try:
1193 features_node = stream_type_node.get(features_prop_name)
1194
1195 if features_node is not None:
1196 pkt_node = features_node.get('packet')
1197
1198 if pkt_node is not None:
1199 set_ft_node_byte_order_prop(pkt_node, 'total-size-field-type')
1200 set_ft_node_byte_order_prop(pkt_node, 'content-size-field-type')
1201 set_ft_node_byte_order_prop(pkt_node, 'beginning-time-field-type')
1202 set_ft_node_byte_order_prop(pkt_node, 'end-time-field-type')
1203 set_ft_node_byte_order_prop(pkt_node,
1204 'discarded-events-counter-field-type')
1205
1206 ev_node = features_node.get('event')
1207
1208 if ev_node is not None:
1209 set_ft_node_byte_order_prop(ev_node, 'type-id-field-type')
1210 set_ft_node_byte_order_prop(ev_node, 'time-field-type')
1211
1212 prop_name = 'packet-context-field-type-extra-members'
1213 pkt_ctx_ft_extra_members_node = stream_type_node.get(prop_name)
1214
1215 if pkt_ctx_ft_extra_members_node is not None:
1216 try:
1217 set_struct_ft_node_members_byte_order_prop(pkt_ctx_ft_extra_members_node)
1218 except _ConfigurationParseError as exc:
1219 _append_error_ctx(exc, f'`{pkt_ctx_ft_extra_members_node}` property')
1220
1221 set_ft_node_byte_order_prop(stream_type_node, 'event-common-context-field-type')
1222
1223 for ev_type_name, ev_type_node in stream_type_node['event-types'].items():
1224 try:
1225 set_ft_node_byte_order_prop(ev_type_node, 'specific-context-field-type')
1226 set_ft_node_byte_order_prop(ev_type_node, 'payload-field-type')
1227 except _ConfigurationParseError as exc:
1228 _append_error_ctx(exc, f'Event type `{ev_type_name}`')
1229 except _ConfigurationParseError as exc:
1230 _append_error_ctx(exc, f'Stream type `{stream_type_name}`')
1231
1232 # Processes the inclusions of the event type node `ev_type_node`,
1233 # returning the effective node.
1234 def _process_ev_type_node_include(self, ev_type_node: _MapNode) -> _MapNode:
1235 # Make sure the event type node is valid for the inclusion
1236 # processing stage.
1237 self._schema_validator.validate(ev_type_node, '3/config/event-type-pre-include')
1238
1239 # process inclusions
1240 return self._process_node_include(ev_type_node, self._process_ev_type_node_include)
1241
1242 # Processes the inclusions of the stream type node
1243 # `stream_type_node`, returning the effective node.
1244 def _process_stream_type_node_include(self, stream_type_node: _MapNode) -> _MapNode:
1245 def process_children_include(stream_type_node: _MapNode):
1246 prop_name = 'event-types'
1247
1248 if prop_name in stream_type_node:
1249 ev_types_node = stream_type_node[prop_name]
1250
1251 for key in list(ev_types_node):
1252 ev_types_node[key] = self._process_ev_type_node_include(ev_types_node[key])
1253
1254 # Make sure the stream type node is valid for the inclusion
1255 # processing stage.
1256 self._schema_validator.validate(stream_type_node, '3/config/stream-type-pre-include')
1257
1258 # process inclusions
1259 return self._process_node_include(stream_type_node, self._process_stream_type_node_include,
1260 process_children_include)
1261
1262 # Processes the inclusions of the clock type node `clk_type_node`,
1263 # returning the effective node.
1264 def _process_clk_type_node_include(self, clk_type_node: _MapNode) -> _MapNode:
1265 # Make sure the clock type node is valid for the inclusion
1266 # processing stage.
1267 self._schema_validator.validate(clk_type_node, '3/config/clock-type-pre-include')
1268
1269 # process inclusions
1270 return self._process_node_include(clk_type_node, self._process_clk_type_node_include)
1271
1272 # Processes the inclusions of the trace type node `trace_type_node`,
1273 # returning the effective node.
1274 def _process_trace_type_node_include(self, trace_type_node: _MapNode) -> _MapNode:
1275 def process_children_include(trace_type_node: _MapNode):
1276 prop_name = 'clock-types'
1277
1278 if prop_name in trace_type_node:
1279 clk_types_node = trace_type_node[prop_name]
1280
1281 for key in list(clk_types_node):
1282 clk_types_node[key] = self._process_clk_type_node_include(clk_types_node[key])
1283
1284 prop_name = 'stream-types'
1285
1286 if prop_name in trace_type_node:
1287 stream_types_node = trace_type_node[prop_name]
1288
1289 for key in list(stream_types_node):
1290 stream_types_node[key] = self._process_stream_type_node_include(stream_types_node[key])
1291
1292 # Make sure the trace type node is valid for the inclusion
1293 # processing stage.
1294 self._schema_validator.validate(trace_type_node, '3/config/trace-type-pre-include')
1295
1296 # process inclusions
1297 return self._process_node_include(trace_type_node, self._process_trace_type_node_include,
1298 process_children_include)
1299
1300 # Processes the inclusions of the trace node `trace_node`, returning
1301 # the effective node.
1302 def _process_trace_node_include(self, trace_node: _MapNode) -> _MapNode:
1303 def process_children_include(trace_node: _MapNode):
1304 prop_name = 'type'
1305 trace_node[prop_name] = self._process_trace_type_node_include(trace_node[prop_name])
1306
1307 # Make sure the trace node is valid for the inclusion processing
1308 # stage.
1309 self._schema_validator.validate(trace_node, '3/config/trace-pre-include')
1310
1311 # process inclusions
1312 return self._process_node_include(trace_node, self._process_trace_node_include,
1313 process_children_include)
1314
1315 # Processes the inclusions of the configuration node, modifying it
1316 # during the process.
1317 def _process_config_includes(self):
1318 # Process inclusions in this order:
1319 #
1320 # 1. Clock type node and event type nodes (the order between
1321 # those is not important).
1322 #
1323 # 2. Stream type nodes.
1324 #
1325 # 3. Trace type node.
1326 #
1327 # 4. Trace node.
1328 #
1329 # This is because:
1330 #
1331 # * A trace node can include a trace type node, clock type
1332 # nodes, stream type nodes, and event type nodes.
1333 #
1334 # * A trace type node can include clock type nodes, stream type
1335 # nodes, and event type nodes.
1336 #
1337 # * A stream type node can include event type nodes.
1338 #
1339 # First, make sure the configuration node itself is valid for
1340 # the inclusion processing stage.
1341 self._schema_validator.validate(self.config_node, '3/config/config-pre-include')
1342
1343 # Process trace node inclusions.
1344 #
1345 # self._process_trace_node_include() returns a new (or the same)
1346 # trace node without any `$include` property in it, recursively.
1347 self.config_node['trace'] = self._process_trace_node_include(self.config_node['trace'])
1348
1349 def _parse(self):
1350 # process configuration node inclusions
1351 self._process_config_includes()
1352
1353 # Expand field type nodes.
1354 #
1355 # This process:
1356 #
1357 # 1. Replaces field type aliases with "effective" field type
1358 # nodes, recursively.
1359 #
1360 # After this step, the `$field-type-aliases` property of the
1361 # trace type node is gone.
1362 #
1363 # 2. Applies inheritance, following the `$inherit` properties.
1364 #
1365 # After this step, field type nodes do not contain `$inherit`
1366 # properties.
1367 #
1368 # This is done blindly, in that the process _doesn't_ validate
1369 # field type nodes at this point.
1370 self._expand_fts()
1371
1372 # Substitute log level aliases.
1373 #
1374 # This process:
1375 #
1376 # 1. Replaces log level aliases in event type nodes with their
1377 # numeric equivalents as found in the `$log-level-aliases`
1378 # property of the trace type node.
1379 #
1380 # 2. Removes the `$log-level-aliases` property from the trace
1381 # type node.
1382 self._sub_log_level_aliases()
1383
1384 # At this point, the configuration node must be valid as an
1385 # effective configuration node.
1386 self._schema_validator.validate(self.config_node, '3/config/config')
1387
1388 # Normalize properties.
1389 #
1390 # This process removes `None` properties and chooses specific
1391 # enumerators when aliases exist (for example, `big-endian`
1392 # instead of `be`).
1393 #
1394 # The goal of this is that, if the user then gets this parser's
1395 # `config_node` property, it has a normal and very readable
1396 # form.
1397 #
1398 # It also makes _create_config() easier to implement because it
1399 # doesn't need to check for `None` nodes or enumerator aliases.
1400 self._normalize_props()
1401
1402 # Set `byte-order` properties of bit array field type nodes
1403 # missing one.
1404 #
1405 # This process also removes the `$default-byte-order` property
1406 # from the trace type node.
1407 self._sub_ft_nodes_byte_order()
1408
1409 # Create a barectf configuration object from the configuration
1410 # node.
1411 self._create_config()
1412
1413 @property
1414 def config(self) -> barectf_config.Configuration:
1415 return self._config
1416
1417 @property
1418 def config_node(self) -> _MapNode:
1419 return typing.cast(barectf_config_parse_common._ConfigNodeV3, self._root_node).config_node
This page took 0.060667 seconds and 4 git commands to generate.