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