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