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